mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Use PyAV fork and set hvc1 codec tag for H.265 (#58309)
This commit is contained in:
parent
f6e38fc4e2
commit
35acca1063
@ -2,7 +2,7 @@
|
||||
"domain": "stream",
|
||||
"name": "Stream",
|
||||
"documentation": "https://www.home-assistant.io/integrations/stream",
|
||||
"requirements": ["av==8.0.3"],
|
||||
"requirements": ["ha-av==8.0.4-rc.1"],
|
||||
"dependencies": ["http"],
|
||||
"codeowners": ["@hunterjm", "@uvjustin", "@allenporter"],
|
||||
"quality_scale": "internal",
|
||||
|
@ -140,6 +140,8 @@ class SegmentBuffer:
|
||||
self._output_video_stream = self._av_output.add_stream(
|
||||
template=self._input_video_stream
|
||||
)
|
||||
if self._output_video_stream.name == "hevc":
|
||||
self._output_video_stream.codec_tag = "hvc1"
|
||||
# Check if audio is requested
|
||||
self._output_audio_stream = None
|
||||
if self._input_audio_stream and self._input_audio_stream.name in AUDIO_CODECS:
|
||||
|
@ -347,9 +347,6 @@ auroranoaa==0.0.2
|
||||
# homeassistant.components.aurora_abb_powerone
|
||||
aurorapy==0.2.6
|
||||
|
||||
# homeassistant.components.stream
|
||||
av==8.0.3
|
||||
|
||||
# homeassistant.components.avea
|
||||
# avea==1.5.1
|
||||
|
||||
@ -767,6 +764,9 @@ gstreamer-player==1.1.2
|
||||
# homeassistant.components.profiler
|
||||
guppy3==3.1.0
|
||||
|
||||
# homeassistant.components.stream
|
||||
ha-av==8.0.4-rc.1
|
||||
|
||||
# homeassistant.components.ffmpeg
|
||||
ha-ffmpeg==3.0.2
|
||||
|
||||
|
@ -238,9 +238,6 @@ auroranoaa==0.0.2
|
||||
# homeassistant.components.aurora_abb_powerone
|
||||
aurorapy==0.2.6
|
||||
|
||||
# homeassistant.components.stream
|
||||
av==8.0.3
|
||||
|
||||
# homeassistant.components.axis
|
||||
axis==44
|
||||
|
||||
@ -466,6 +463,9 @@ growattServer==1.1.0
|
||||
# homeassistant.components.profiler
|
||||
guppy3==3.1.0
|
||||
|
||||
# homeassistant.components.stream
|
||||
ha-av==8.0.4-rc.1
|
||||
|
||||
# homeassistant.components.ffmpeg
|
||||
ha-ffmpeg==3.0.2
|
||||
|
||||
|
@ -37,7 +37,7 @@ def generate_audio_frame(pcm_mulaw=False):
|
||||
return audio_frame
|
||||
|
||||
|
||||
def generate_h264_video(container_format="mp4", duration=5):
|
||||
def generate_video(encoder, container_format, duration):
|
||||
"""
|
||||
Generate a test video.
|
||||
|
||||
@ -51,7 +51,7 @@ def generate_h264_video(container_format="mp4", duration=5):
|
||||
output.name = "test.mov" if container_format == "mov" else "test.mp4"
|
||||
container = av.open(output, mode="w", format=container_format)
|
||||
|
||||
stream = container.add_stream("libx264", rate=fps)
|
||||
stream = container.add_stream(encoder, rate=fps)
|
||||
stream.width = 480
|
||||
stream.height = 320
|
||||
stream.pix_fmt = "yuv420p"
|
||||
@ -82,6 +82,16 @@ def generate_h264_video(container_format="mp4", duration=5):
|
||||
return output
|
||||
|
||||
|
||||
def generate_h264_video(container_format="mp4", duration=5):
|
||||
"""Generate a test video with libx264."""
|
||||
return generate_video("libx264", container_format, duration)
|
||||
|
||||
|
||||
def generate_h265_video(container_format="mp4", duration=5):
|
||||
"""Generate a test video with libx265."""
|
||||
return generate_video("libx265", container_format, duration)
|
||||
|
||||
|
||||
def remux_with_audio(source, container_format, audio_codec):
|
||||
"""Remux an existing source with new audio."""
|
||||
av_source = av.open(source, mode="r")
|
||||
|
@ -40,7 +40,7 @@ from homeassistant.components.stream.core import StreamSettings
|
||||
from homeassistant.components.stream.worker import SegmentBuffer, stream_worker
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.components.stream.common import generate_h264_video
|
||||
from tests.components.stream.common import generate_h264_video, generate_h265_video
|
||||
from tests.components.stream.test_ll_hls import TEST_PART_DURATION
|
||||
|
||||
STREAM_SOURCE = "some-stream-source"
|
||||
@ -201,6 +201,9 @@ class FakePyAvBuffer:
|
||||
def __str__(self) -> str:
|
||||
return f"FakeAvOutputStream<{template.name}>"
|
||||
|
||||
def name(self) -> str:
|
||||
return "avc1"
|
||||
|
||||
if template.name == AUDIO_STREAM_FORMAT:
|
||||
return FakeAvOutputStream(self.audio_packets)
|
||||
return FakeAvOutputStream(self.video_packets)
|
||||
@ -771,3 +774,38 @@ async def test_has_keyframe(hass, record_worker_sync):
|
||||
await record_worker_sync.join()
|
||||
|
||||
stream.stop()
|
||||
|
||||
|
||||
async def test_h265_video_is_hvc1(hass, record_worker_sync):
|
||||
"""Test that a h265 video gets muxed as hvc1."""
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"stream",
|
||||
{
|
||||
"stream": {
|
||||
CONF_LL_HLS: True,
|
||||
CONF_SEGMENT_DURATION: SEGMENT_DURATION,
|
||||
CONF_PART_DURATION: TEST_PART_DURATION,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
source = generate_h265_video()
|
||||
stream = create_stream(hass, source, {})
|
||||
|
||||
# use record_worker_sync to grab output segments
|
||||
with patch.object(hass.config, "is_allowed_path", return_value=True):
|
||||
await stream.async_record("/example/path")
|
||||
|
||||
complete_segments = list(await record_worker_sync.get_segments())[:-1]
|
||||
assert len(complete_segments) >= 1
|
||||
|
||||
segment = complete_segments[0]
|
||||
part = segment.parts[0]
|
||||
av_part = av.open(io.BytesIO(segment.init + part.data))
|
||||
assert av_part.streams.video[0].codec_tag == "hvc1"
|
||||
av_part.close()
|
||||
|
||||
await record_worker_sync.join()
|
||||
|
||||
stream.stop()
|
||||
|
Loading…
x
Reference in New Issue
Block a user