Switch back to av 13.1.0 (#129699)

This commit is contained in:
Marc Mueller 2024-11-04 14:48:28 +01:00 committed by GitHub
parent ff621d5bf3
commit 41a81cbf15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 47 additions and 49 deletions

View File

@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/generic", "documentation": "https://www.home-assistant.io/integrations/generic",
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"requirements": ["ha-av==10.1.1", "Pillow==10.4.0"] "requirements": ["av==13.1.0", "Pillow==10.4.0"]
} }

View File

@ -27,8 +27,7 @@ from .const import (
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from av import Packet from av import Packet, VideoCodecContext
from av.video.codeccontext import VideoCodecContext
from homeassistant.components.camera import DynamicStreamSettings from homeassistant.components.camera import DynamicStreamSettings
@ -509,9 +508,8 @@ class KeyFrameConverter:
frames = self._codec_context.decode(None) frames = self._codec_context.decode(None)
break break
except EOFError: except EOFError:
_LOGGER.debug("Codec context needs flushing, attempting to reopen") _LOGGER.debug("Codec context needs flushing")
self._codec_context.close() self._codec_context.flush_buffers()
self._codec_context.open()
else: else:
_LOGGER.debug("Unable to decode keyframe") _LOGGER.debug("Unable to decode keyframe")
return return

View File

@ -7,5 +7,5 @@
"integration_type": "system", "integration_type": "system",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["PyTurboJPEG==1.7.5", "ha-av==10.1.1", "numpy==1.26.4"] "requirements": ["PyTurboJPEG==1.7.5", "av==13.1.0", "numpy==1.26.4"]
} }

View File

@ -107,7 +107,7 @@ class RecorderOutput(StreamOutput):
# Create output on first segment # Create output on first segment
if not output: if not output:
container_options: dict[str, str] = { container_options: dict[str, str] = {
"video_track_timescale": str(int(1 / source_v.time_base)), "video_track_timescale": str(int(1 / source_v.time_base)), # type: ignore[operator]
"movflags": "frag_keyframe+empty_moov", "movflags": "frag_keyframe+empty_moov",
"min_frag_duration": str(self.stream_settings.min_segment_duration), "min_frag_duration": str(self.stream_settings.min_segment_duration),
} }
@ -132,21 +132,23 @@ class RecorderOutput(StreamOutput):
last_stream_id = segment.stream_id last_stream_id = segment.stream_id
pts_adjuster["video"] = int( pts_adjuster["video"] = int(
(running_duration - source.start_time) (running_duration - source.start_time)
/ (av.time_base * source_v.time_base) / (av.time_base * source_v.time_base) # type: ignore[operator]
) )
if source_a: if source_a:
pts_adjuster["audio"] = int( pts_adjuster["audio"] = int(
(running_duration - source.start_time) (running_duration - source.start_time)
/ (av.time_base * source_a.time_base) / (av.time_base * source_a.time_base) # type: ignore[operator]
) )
# Remux video # Remux video
for packet in source.demux(): for packet in source.demux():
if packet.dts is None: if packet.pts is None:
continue continue
packet.pts += pts_adjuster[packet.stream.type] packet.pts += pts_adjuster[packet.stream.type] # type: ignore[operator]
packet.dts += pts_adjuster[packet.stream.type] packet.dts += pts_adjuster[packet.stream.type] # type: ignore[operator]
packet.stream = output_v if packet.stream.type == "video" else output_a stream = output_v if packet.stream.type == "video" else output_a
assert stream
packet.stream = stream
output.mux(packet) output.mux(packet)
running_duration += source.duration - source.start_time running_duration += source.duration - source.start_time

View File

@ -16,7 +16,6 @@ import av
import av.audio import av.audio
import av.container import av.container
import av.stream import av.stream
import av.video
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -53,8 +52,8 @@ class StreamWorkerError(Exception):
def redact_av_error_string(err: av.FFmpegError) -> str: def redact_av_error_string(err: av.FFmpegError) -> str:
"""Return an error string with credentials redacted from the url.""" """Return an error string with credentials redacted from the url."""
parts = [str(err.type), err.strerror] parts = [str(err.type), err.strerror] # type: ignore[attr-defined]
if err.filename is not None: if err.filename:
parts.append(redact_credentials(err.filename)) parts.append(redact_credentials(err.filename))
return ", ".join(parts) return ", ".join(parts)
@ -130,19 +129,19 @@ class StreamMuxer:
_segment_start_dts: int _segment_start_dts: int
_memory_file: BytesIO _memory_file: BytesIO
_av_output: av.container.OutputContainer _av_output: av.container.OutputContainer
_output_video_stream: av.video.VideoStream _output_video_stream: av.VideoStream
_output_audio_stream: av.audio.AudioStream | None _output_audio_stream: av.audio.AudioStream | None
_segment: Segment | None _segment: Segment | None
# the following 2 member variables are used for Part formation # the following 2 member variables are used for Part formation
_memory_file_pos: int _memory_file_pos: int
_part_start_dts: int _part_start_dts: float
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
video_stream: av.video.VideoStream, video_stream: av.VideoStream,
audio_stream: av.audio.AudioStream | None, audio_stream: av.audio.AudioStream | None,
audio_bsf: av.BitStreamFilter | None, audio_bsf: str | None,
stream_state: StreamState, stream_state: StreamState,
stream_settings: StreamSettings, stream_settings: StreamSettings,
) -> None: ) -> None:
@ -161,11 +160,11 @@ class StreamMuxer:
self, self,
memory_file: BytesIO, memory_file: BytesIO,
sequence: int, sequence: int,
input_vstream: av.video.VideoStream, input_vstream: av.VideoStream,
input_astream: av.audio.AudioStream | None, input_astream: av.audio.AudioStream | None,
) -> tuple[ ) -> tuple[
av.container.OutputContainer, av.container.OutputContainer,
av.video.VideoStream, av.VideoStream,
av.audio.AudioStream | None, av.audio.AudioStream | None,
]: ]:
"""Make a new av OutputContainer and add output streams.""" """Make a new av OutputContainer and add output streams."""
@ -182,7 +181,7 @@ class StreamMuxer:
# in test_durations # in test_durations
"avoid_negative_ts": "make_non_negative", "avoid_negative_ts": "make_non_negative",
"fragment_index": str(sequence + 1), "fragment_index": str(sequence + 1),
"video_track_timescale": str(int(1 / input_vstream.time_base)), "video_track_timescale": str(int(1 / input_vstream.time_base)), # type: ignore[operator]
# Only do extra fragmenting if we are using ll_hls # Only do extra fragmenting if we are using ll_hls
# Let ffmpeg do the work using frag_duration # Let ffmpeg do the work using frag_duration
# Fragment durations may exceed the 15% allowed variance but it seems ok # Fragment durations may exceed the 15% allowed variance but it seems ok
@ -233,12 +232,11 @@ class StreamMuxer:
output_astream = None output_astream = None
if input_astream: if input_astream:
if self._audio_bsf: if self._audio_bsf:
self._audio_bsf_context = self._audio_bsf.create() self._audio_bsf_context = av.BitStreamFilterContext(
self._audio_bsf_context.set_input_stream(input_astream) self._audio_bsf, input_astream
output_astream = container.add_stream( )
template=self._audio_bsf_context or input_astream output_astream = container.add_stream(template=input_astream)
) return container, output_vstream, output_astream # type: ignore[return-value]
return container, output_vstream, output_astream
def reset(self, video_dts: int) -> None: def reset(self, video_dts: int) -> None:
"""Initialize a new stream segment.""" """Initialize a new stream segment."""
@ -279,11 +277,11 @@ class StreamMuxer:
self._part_has_keyframe |= packet.is_keyframe self._part_has_keyframe |= packet.is_keyframe
elif packet.stream == self._input_audio_stream: elif packet.stream == self._input_audio_stream:
assert self._output_audio_stream
if self._audio_bsf_context: if self._audio_bsf_context:
self._audio_bsf_context.send(packet) for audio_packet in self._audio_bsf_context.filter(packet):
while packet := self._audio_bsf_context.recv(): audio_packet.stream = self._output_audio_stream
packet.stream = self._output_audio_stream self._av_output.mux(audio_packet)
self._av_output.mux(packet)
return return
packet.stream = self._output_audio_stream packet.stream = self._output_audio_stream
self._av_output.mux(packet) self._av_output.mux(packet)
@ -465,7 +463,7 @@ class TimestampValidator:
"""Validate the packet timestamp based on ordering within the stream.""" """Validate the packet timestamp based on ordering within the stream."""
# Discard packets missing DTS. Terminate if too many are missing. # Discard packets missing DTS. Terminate if too many are missing.
if packet.dts is None: if packet.dts is None:
if self._missing_dts >= MAX_MISSING_DTS: if self._missing_dts >= MAX_MISSING_DTS: # type: ignore[unreachable]
raise StreamWorkerError( raise StreamWorkerError(
f"No dts in {MAX_MISSING_DTS+1} consecutive packets" f"No dts in {MAX_MISSING_DTS+1} consecutive packets"
) )
@ -492,7 +490,7 @@ def is_keyframe(packet: av.Packet) -> Any:
def get_audio_bitstream_filter( def get_audio_bitstream_filter(
packets: Iterator[av.Packet], audio_stream: Any packets: Iterator[av.Packet], audio_stream: Any
) -> av.BitStreamFilterContext | None: ) -> str | None:
"""Return the aac_adtstoasc bitstream filter if ADTS AAC is detected.""" """Return the aac_adtstoasc bitstream filter if ADTS AAC is detected."""
if not audio_stream: if not audio_stream:
return None return None
@ -509,7 +507,7 @@ def get_audio_bitstream_filter(
_LOGGER.debug( _LOGGER.debug(
"ADTS AAC detected. Adding aac_adtstoaac bitstream filter" "ADTS AAC detected. Adding aac_adtstoaac bitstream filter"
) )
return av.BitStreamFilter("aac_adtstoasc") return "aac_adtstoasc"
break break
return None return None
@ -547,7 +545,7 @@ def stream_worker(
audio_stream = None audio_stream = None
# Some audio streams do not have a profile and throw errors when remuxing # Some audio streams do not have a profile and throw errors when remuxing
if audio_stream and audio_stream.profile is None: if audio_stream and audio_stream.profile is None:
audio_stream = None audio_stream = None # type: ignore[unreachable]
# Disable ll-hls for hls inputs # Disable ll-hls for hls inputs
if container.format.name == "hls": if container.format.name == "hls":
for field in fields(StreamSettings): for field in fields(StreamSettings):
@ -562,8 +560,8 @@ def stream_worker(
stream_state.diagnostics.set_value("audio_codec", audio_stream.name) stream_state.diagnostics.set_value("audio_codec", audio_stream.name)
dts_validator = TimestampValidator( dts_validator = TimestampValidator(
int(1 / video_stream.time_base), int(1 / video_stream.time_base), # type: ignore[operator]
int(1 / audio_stream.time_base) if audio_stream else 1, int(1 / audio_stream.time_base) if audio_stream else 1, # type: ignore[operator]
) )
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)))

View File

@ -13,6 +13,7 @@ async-interrupt==1.2.0
async-upnp-client==0.41.0 async-upnp-client==0.41.0
atomicwrites-homeassistant==1.4.1 atomicwrites-homeassistant==1.4.1
attrs==24.2.0 attrs==24.2.0
av==13.1.0
awesomeversion==24.6.0 awesomeversion==24.6.0
bcrypt==4.2.0 bcrypt==4.2.0
bleak-retry-connector==3.6.0 bleak-retry-connector==3.6.0
@ -27,7 +28,6 @@ cryptography==43.0.1
dbus-fast==2.24.3 dbus-fast==2.24.3
fnv-hash-fast==1.0.2 fnv-hash-fast==1.0.2
go2rtc-client==0.0.1b3 go2rtc-client==0.0.1b3
ha-av==10.1.1
ha-ffmpeg==3.2.1 ha-ffmpeg==3.2.1
habluetooth==3.6.0 habluetooth==3.6.0
hass-nabucasa==0.83.0 hass-nabucasa==0.83.0

View File

@ -526,6 +526,10 @@ autarco==3.1.0
# homeassistant.components.husqvarna_automower_ble # homeassistant.components.husqvarna_automower_ble
automower-ble==0.2.0 automower-ble==0.2.0
# homeassistant.components.generic
# homeassistant.components.stream
av==13.1.0
# homeassistant.components.avea # homeassistant.components.avea
# avea==1.5.1 # avea==1.5.1
@ -1064,10 +1068,6 @@ guppy3==3.1.4.post1
# homeassistant.components.iaqualink # homeassistant.components.iaqualink
h2==4.1.0 h2==4.1.0
# homeassistant.components.generic
# homeassistant.components.stream
ha-av==10.1.1
# homeassistant.components.ffmpeg # homeassistant.components.ffmpeg
ha-ffmpeg==3.2.1 ha-ffmpeg==3.2.1

View File

@ -481,6 +481,10 @@ autarco==3.1.0
# homeassistant.components.husqvarna_automower_ble # homeassistant.components.husqvarna_automower_ble
automower-ble==0.2.0 automower-ble==0.2.0
# homeassistant.components.generic
# homeassistant.components.stream
av==13.1.0
# homeassistant.components.axis # homeassistant.components.axis
axis==63 axis==63
@ -902,10 +906,6 @@ guppy3==3.1.4.post1
# homeassistant.components.iaqualink # homeassistant.components.iaqualink
h2==4.1.0 h2==4.1.0
# homeassistant.components.generic
# homeassistant.components.stream
ha-av==10.1.1
# homeassistant.components.ffmpeg # homeassistant.components.ffmpeg
ha-ffmpeg==3.2.1 ha-ffmpeg==3.2.1