Enable Low Latency HLS (LL-HLS) by default to lower stream latency (#64643)

This commit is contained in:
Allen Porter 2022-01-23 06:38:29 -08:00 committed by GitHub
parent 4e376181f5
commit e74fe0e390
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 40 deletions

View file

@ -97,19 +97,21 @@ def create_stream(
return stream
DOMAIN_SCHEMA = vol.Schema(
{
vol.Optional(CONF_LL_HLS, default=True): cv.boolean,
vol.Optional(CONF_SEGMENT_DURATION, default=6): vol.All(
cv.positive_float, vol.Range(min=2, max=10)
),
vol.Optional(CONF_PART_DURATION, default=1): vol.All(
cv.positive_float, vol.Range(min=0.2, max=1.5)
),
}
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_LL_HLS, default=False): cv.boolean,
vol.Optional(CONF_SEGMENT_DURATION, default=6): vol.All(
cv.positive_float, vol.Range(min=2, max=10)
),
vol.Optional(CONF_PART_DURATION, default=1): vol.All(
cv.positive_float, vol.Range(min=0.2, max=1.5)
),
}
)
DOMAIN: DOMAIN_SCHEMA,
},
extra=vol.ALLOW_EXTRA,
)
@ -154,7 +156,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DOMAIN] = {}
hass.data[DOMAIN][ATTR_ENDPOINTS] = {}
hass.data[DOMAIN][ATTR_STREAMS] = []
if (conf := config.get(DOMAIN)) and conf[CONF_LL_HLS]:
conf = DOMAIN_SCHEMA(config.get(DOMAIN, {}))
if conf[CONF_LL_HLS]:
assert isinstance(conf[CONF_SEGMENT_DURATION], float)
assert isinstance(conf[CONF_PART_DURATION], float)
hass.data[DOMAIN][ATTR_SETTINGS] = StreamSettings(

View file

@ -29,6 +29,18 @@ SEGMENT_DURATION = 10
TEST_TIMEOUT = 5.0 # Lower than 9s home assistant timeout
MAX_ABORT_SEGMENTS = 20 # Abort test to avoid looping forever
HLS_CONFIG = {
"stream": {
"ll_hls": False,
}
}
@pytest.fixture
async def setup_component(hass) -> None:
"""Test fixture to setup the stream component."""
await async_setup_component(hass, "stream", HLS_CONFIG)
class HlsClient:
"""Test fixture for fetching the hls stream."""
@ -114,14 +126,15 @@ def make_playlist(
return "\n".join(response)
async def test_hls_stream(hass, hls_stream, stream_worker_sync, h264_video):
async def test_hls_stream(
hass, setup_component, hls_stream, stream_worker_sync, h264_video
):
"""
Test hls stream.
Purposefully not mocking anything here to test full
integration with the stream component.
"""
await async_setup_component(hass, "stream", {"stream": {}})
stream_worker_sync.pause()
@ -164,10 +177,10 @@ async def test_hls_stream(hass, hls_stream, stream_worker_sync, h264_video):
assert fail_response.status == HTTPStatus.NOT_FOUND
async def test_stream_timeout(hass, hass_client, stream_worker_sync, h264_video):
async def test_stream_timeout(
hass, hass_client, setup_component, stream_worker_sync, h264_video
):
"""Test hls stream timeout."""
await async_setup_component(hass, "stream", {"stream": {}})
stream_worker_sync.pause()
# Setup demo HLS track
@ -217,11 +230,9 @@ async def test_stream_timeout(hass, hass_client, stream_worker_sync, h264_video)
async def test_stream_timeout_after_stop(
hass, hass_client, stream_worker_sync, h264_video
hass, hass_client, setup_component, stream_worker_sync, h264_video
):
"""Test hls stream timeout after the stream has been stopped already."""
await async_setup_component(hass, "stream", {"stream": {}})
stream_worker_sync.pause()
# Setup demo HLS track
@ -241,10 +252,8 @@ async def test_stream_timeout_after_stop(
await hass.async_block_till_done()
async def test_stream_keepalive(hass):
async def test_stream_keepalive(hass, setup_component):
"""Test hls stream retries the stream when keepalive=True."""
await async_setup_component(hass, "stream", {"stream": {}})
# Setup demo HLS track
source = "test_stream_keepalive_source"
stream = create_stream(hass, source, {})
@ -291,10 +300,8 @@ async def test_stream_keepalive(hass):
assert available_states == [True, False, True]
async def test_hls_playlist_view_no_output(hass, hls_stream):
async def test_hls_playlist_view_no_output(hass, setup_component, hls_stream):
"""Test rendering the hls playlist with no output segments."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream.add_provider(HLS_PROVIDER)
@ -305,10 +312,8 @@ async def test_hls_playlist_view_no_output(hass, hls_stream):
assert resp.status == HTTPStatus.NOT_FOUND
async def test_hls_playlist_view(hass, hls_stream, stream_worker_sync):
async def test_hls_playlist_view(hass, setup_component, hls_stream, stream_worker_sync):
"""Test rendering the hls playlist with 1 and 2 output segments."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream_worker_sync.pause()
hls = stream.add_provider(HLS_PROVIDER)
@ -338,10 +343,8 @@ async def test_hls_playlist_view(hass, hls_stream, stream_worker_sync):
stream.stop()
async def test_hls_max_segments(hass, hls_stream, stream_worker_sync):
async def test_hls_max_segments(hass, setup_component, hls_stream, stream_worker_sync):
"""Test rendering the hls playlist with more segments than the segment deque can hold."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream_worker_sync.pause()
hls = stream.add_provider(HLS_PROVIDER)
@ -389,9 +392,10 @@ async def test_hls_max_segments(hass, hls_stream, stream_worker_sync):
stream.stop()
async def test_hls_playlist_view_discontinuity(hass, hls_stream, stream_worker_sync):
async def test_hls_playlist_view_discontinuity(
hass, setup_component, hls_stream, stream_worker_sync
):
"""Test a discontinuity across segments in the stream with 3 segments."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream_worker_sync.pause()
@ -426,10 +430,10 @@ async def test_hls_playlist_view_discontinuity(hass, hls_stream, stream_worker_s
stream.stop()
async def test_hls_max_segments_discontinuity(hass, hls_stream, stream_worker_sync):
async def test_hls_max_segments_discontinuity(
hass, setup_component, hls_stream, stream_worker_sync
):
"""Test a discontinuity with more segments than the segment deque can hold."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream_worker_sync.pause()
hls = stream.add_provider(HLS_PROVIDER)
@ -469,10 +473,10 @@ async def test_hls_max_segments_discontinuity(hass, hls_stream, stream_worker_sy
stream.stop()
async def test_remove_incomplete_segment_on_exit(hass, stream_worker_sync):
async def test_remove_incomplete_segment_on_exit(
hass, setup_component, stream_worker_sync
):
"""Test that the incomplete segment gets removed when the worker thread quits."""
await async_setup_component(hass, "stream", {"stream": {}})
stream = create_stream(hass, STREAM_SOURCE, {})
stream_worker_sync.pause()
stream.start()