mirror of
https://github.com/home-assistant/core.git
synced 2025-07-10 14:57:09 +00:00
Decouple stream options from PyAV options (#71247)
Co-authored-by: Allen Porter <allen.porter@gmail.com>
This commit is contained in:
parent
e0bf1fba8d
commit
617b0d04dc
@ -12,6 +12,11 @@ from homeassistant.components.camera import (
|
|||||||
Camera,
|
Camera,
|
||||||
CameraEntityFeature,
|
CameraEntityFeature,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.stream.const import (
|
||||||
|
CONF_RTSP_TRANSPORT,
|
||||||
|
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
||||||
|
RTSP_TRANSPORTS,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_AUTHENTICATION,
|
CONF_AUTHENTICATION,
|
||||||
@ -34,14 +39,10 @@ from .const import (
|
|||||||
CONF_CONTENT_TYPE,
|
CONF_CONTENT_TYPE,
|
||||||
CONF_FRAMERATE,
|
CONF_FRAMERATE,
|
||||||
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
CONF_STILL_IMAGE_URL,
|
CONF_STILL_IMAGE_URL,
|
||||||
CONF_STREAM_SOURCE,
|
CONF_STREAM_SOURCE,
|
||||||
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
FFMPEG_OPTION_MAP,
|
|
||||||
GET_IMAGE_TIMEOUT,
|
GET_IMAGE_TIMEOUT,
|
||||||
RTSP_TRANSPORTS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -63,7 +64,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
cv.small_float, cv.positive_int
|
cv.small_float, cv.positive_int
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
|
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
|
||||||
vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS.keys()),
|
vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -157,14 +158,10 @@ class GenericCamera(Camera):
|
|||||||
self.content_type = device_info[CONF_CONTENT_TYPE]
|
self.content_type = device_info[CONF_CONTENT_TYPE]
|
||||||
self.verify_ssl = device_info[CONF_VERIFY_SSL]
|
self.verify_ssl = device_info[CONF_VERIFY_SSL]
|
||||||
if device_info.get(CONF_RTSP_TRANSPORT):
|
if device_info.get(CONF_RTSP_TRANSPORT):
|
||||||
self.stream_options[FFMPEG_OPTION_MAP[CONF_RTSP_TRANSPORT]] = device_info[
|
self.stream_options[CONF_RTSP_TRANSPORT] = device_info[CONF_RTSP_TRANSPORT]
|
||||||
CONF_RTSP_TRANSPORT
|
|
||||||
]
|
|
||||||
self._auth = generate_auth(device_info)
|
self._auth = generate_auth(device_info)
|
||||||
if device_info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
if device_info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
||||||
self.stream_options[
|
self.stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True
|
||||||
FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS]
|
|
||||||
] = "1"
|
|
||||||
|
|
||||||
self._last_url = None
|
self._last_url = None
|
||||||
self._last_image = None
|
self._last_image = None
|
||||||
|
@ -16,7 +16,12 @@ from httpx import HTTPStatusError, RequestError, TimeoutException
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yarl
|
import yarl
|
||||||
|
|
||||||
from homeassistant.components.stream.const import SOURCE_TIMEOUT
|
from homeassistant.components.stream.const import (
|
||||||
|
CONF_RTSP_TRANSPORT,
|
||||||
|
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
||||||
|
RTSP_TRANSPORTS,
|
||||||
|
SOURCE_TIMEOUT,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_AUTHENTICATION,
|
CONF_AUTHENTICATION,
|
||||||
@ -38,15 +43,11 @@ from .const import (
|
|||||||
CONF_CONTENT_TYPE,
|
CONF_CONTENT_TYPE,
|
||||||
CONF_FRAMERATE,
|
CONF_FRAMERATE,
|
||||||
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
CONF_STILL_IMAGE_URL,
|
CONF_STILL_IMAGE_URL,
|
||||||
CONF_STREAM_SOURCE,
|
CONF_STREAM_SOURCE,
|
||||||
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
FFMPEG_OPTION_MAP,
|
|
||||||
GET_IMAGE_TIMEOUT,
|
GET_IMAGE_TIMEOUT,
|
||||||
RTSP_TRANSPORTS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -200,16 +201,16 @@ async def async_test_stream(hass, info) -> dict[str, str]:
|
|||||||
# For RTSP streams, prefer TCP. This code is duplicated from
|
# For RTSP streams, prefer TCP. This code is duplicated from
|
||||||
# homeassistant.components.stream.__init__.py:create_stream()
|
# homeassistant.components.stream.__init__.py:create_stream()
|
||||||
# It may be possible & better to call create_stream() directly.
|
# It may be possible & better to call create_stream() directly.
|
||||||
stream_options: dict[str, str] = {}
|
stream_options: dict[str, bool | str] = {}
|
||||||
if isinstance(stream_source, str) and stream_source[:7] == "rtsp://":
|
if isinstance(stream_source, str) and stream_source[:7] == "rtsp://":
|
||||||
stream_options = {
|
stream_options = {
|
||||||
"rtsp_flags": "prefer_tcp",
|
"rtsp_flags": "prefer_tcp",
|
||||||
"stimeout": "5000000",
|
"stimeout": "5000000",
|
||||||
}
|
}
|
||||||
if rtsp_transport := info.get(CONF_RTSP_TRANSPORT):
|
if rtsp_transport := info.get(CONF_RTSP_TRANSPORT):
|
||||||
stream_options[FFMPEG_OPTION_MAP[CONF_RTSP_TRANSPORT]] = rtsp_transport
|
stream_options[CONF_RTSP_TRANSPORT] = rtsp_transport
|
||||||
if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
||||||
stream_options[FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS]] = "1"
|
stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True
|
||||||
_LOGGER.debug("Attempting to open stream %s", stream_source)
|
_LOGGER.debug("Attempting to open stream %s", stream_source)
|
||||||
container = await hass.async_add_executor_job(
|
container = await hass.async_add_executor_job(
|
||||||
partial(
|
partial(
|
||||||
|
@ -7,18 +7,6 @@ CONF_LIMIT_REFETCH_TO_URL_CHANGE = "limit_refetch_to_url_change"
|
|||||||
CONF_STILL_IMAGE_URL = "still_image_url"
|
CONF_STILL_IMAGE_URL = "still_image_url"
|
||||||
CONF_STREAM_SOURCE = "stream_source"
|
CONF_STREAM_SOURCE = "stream_source"
|
||||||
CONF_FRAMERATE = "framerate"
|
CONF_FRAMERATE = "framerate"
|
||||||
CONF_RTSP_TRANSPORT = "rtsp_transport"
|
|
||||||
CONF_USE_WALLCLOCK_AS_TIMESTAMPS = "use_wallclock_as_timestamps"
|
|
||||||
FFMPEG_OPTION_MAP = {
|
|
||||||
CONF_RTSP_TRANSPORT: "rtsp_transport",
|
|
||||||
CONF_USE_WALLCLOCK_AS_TIMESTAMPS: "use_wallclock_as_timestamps",
|
|
||||||
}
|
|
||||||
RTSP_TRANSPORTS = {
|
|
||||||
"tcp": "TCP",
|
|
||||||
"udp": "UDP",
|
|
||||||
"udp_multicast": "UDP Multicast",
|
|
||||||
"http": "HTTP",
|
|
||||||
}
|
|
||||||
GET_IMAGE_TIMEOUT = 10
|
GET_IMAGE_TIMEOUT = 10
|
||||||
|
|
||||||
DEFAULT_USERNAME = None
|
DEFAULT_USERNAME = None
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from onvif.exceptions import ONVIFAuthError, ONVIFError, ONVIFTimeoutError
|
from onvif.exceptions import ONVIFAuthError, ONVIFError, ONVIFTimeoutError
|
||||||
|
|
||||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
||||||
|
from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
@ -12,13 +13,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
from .const import (
|
from .const import CONF_SNAPSHOT_AUTH, DEFAULT_ARGUMENTS, DOMAIN
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
CONF_SNAPSHOT_AUTH,
|
|
||||||
DEFAULT_ARGUMENTS,
|
|
||||||
DOMAIN,
|
|
||||||
RTSP_TRANS_PROTOCOLS,
|
|
||||||
)
|
|
||||||
from .device import ONVIFDevice
|
from .device import ONVIFDevice
|
||||||
|
|
||||||
|
|
||||||
@ -99,7 +94,7 @@ async def async_populate_options(hass, entry):
|
|||||||
"""Populate default options for device."""
|
"""Populate default options for device."""
|
||||||
options = {
|
options = {
|
||||||
CONF_EXTRA_ARGUMENTS: DEFAULT_ARGUMENTS,
|
CONF_EXTRA_ARGUMENTS: DEFAULT_ARGUMENTS,
|
||||||
CONF_RTSP_TRANSPORT: RTSP_TRANS_PROTOCOLS[0],
|
CONF_RTSP_TRANSPORT: next(iter(RTSP_TRANSPORTS)),
|
||||||
}
|
}
|
||||||
|
|
||||||
hass.config_entries.async_update_entry(entry, options=options)
|
hass.config_entries.async_update_entry(entry, options=options)
|
||||||
|
@ -9,6 +9,7 @@ from yarl import URL
|
|||||||
from homeassistant.components import ffmpeg
|
from homeassistant.components import ffmpeg
|
||||||
from homeassistant.components.camera import Camera, CameraEntityFeature
|
from homeassistant.components.camera import Camera, CameraEntityFeature
|
||||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, get_ffmpeg_manager
|
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, get_ffmpeg_manager
|
||||||
|
from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import HTTP_BASIC_AUTHENTICATION
|
from homeassistant.const import HTTP_BASIC_AUTHENTICATION
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -27,7 +28,6 @@ from .const import (
|
|||||||
ATTR_SPEED,
|
ATTR_SPEED,
|
||||||
ATTR_TILT,
|
ATTR_TILT,
|
||||||
ATTR_ZOOM,
|
ATTR_ZOOM,
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
CONF_SNAPSHOT_AUTH,
|
CONF_SNAPSHOT_AUTH,
|
||||||
CONTINUOUS_MOVE,
|
CONTINUOUS_MOVE,
|
||||||
DIR_DOWN,
|
DIR_DOWN,
|
||||||
|
@ -13,6 +13,7 @@ from zeep.exceptions import Fault
|
|||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS
|
||||||
|
from homeassistant.components.stream import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
@ -22,15 +23,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from .const import (
|
from .const import CONF_DEVICE_ID, DEFAULT_ARGUMENTS, DEFAULT_PORT, DOMAIN, LOGGER
|
||||||
CONF_DEVICE_ID,
|
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
DEFAULT_ARGUMENTS,
|
|
||||||
DEFAULT_PORT,
|
|
||||||
DOMAIN,
|
|
||||||
LOGGER,
|
|
||||||
RTSP_TRANS_PROTOCOLS,
|
|
||||||
)
|
|
||||||
from .device import get_device
|
from .device import get_device
|
||||||
|
|
||||||
CONF_MANUAL_INPUT = "Manually configure ONVIF device"
|
CONF_MANUAL_INPUT = "Manually configure ONVIF device"
|
||||||
@ -294,9 +287,9 @@ class OnvifOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_RTSP_TRANSPORT,
|
CONF_RTSP_TRANSPORT,
|
||||||
default=self.config_entry.options.get(
|
default=self.config_entry.options.get(
|
||||||
CONF_RTSP_TRANSPORT, RTSP_TRANS_PROTOCOLS[0]
|
CONF_RTSP_TRANSPORT, next(iter(RTSP_TRANSPORTS))
|
||||||
),
|
),
|
||||||
): vol.In(RTSP_TRANS_PROTOCOLS),
|
): vol.In(RTSP_TRANSPORTS),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -9,11 +9,8 @@ DEFAULT_PORT = 80
|
|||||||
DEFAULT_ARGUMENTS = "-pred 1"
|
DEFAULT_ARGUMENTS = "-pred 1"
|
||||||
|
|
||||||
CONF_DEVICE_ID = "deviceid"
|
CONF_DEVICE_ID = "deviceid"
|
||||||
CONF_RTSP_TRANSPORT = "rtsp_transport"
|
|
||||||
CONF_SNAPSHOT_AUTH = "snapshot_auth"
|
CONF_SNAPSHOT_AUTH = "snapshot_auth"
|
||||||
|
|
||||||
RTSP_TRANS_PROTOCOLS = ["tcp", "udp", "udp_multicast", "http"]
|
|
||||||
|
|
||||||
ATTR_PAN = "pan"
|
ATTR_PAN = "pan"
|
||||||
ATTR_TILT = "tilt"
|
ATTR_TILT = "tilt"
|
||||||
ATTR_ZOOM = "zoom"
|
ATTR_ZOOM = "zoom"
|
||||||
|
@ -23,7 +23,7 @@ import secrets
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
from typing import Any, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -39,12 +39,15 @@ from .const import (
|
|||||||
ATTR_STREAMS,
|
ATTR_STREAMS,
|
||||||
CONF_LL_HLS,
|
CONF_LL_HLS,
|
||||||
CONF_PART_DURATION,
|
CONF_PART_DURATION,
|
||||||
|
CONF_RTSP_TRANSPORT,
|
||||||
CONF_SEGMENT_DURATION,
|
CONF_SEGMENT_DURATION,
|
||||||
|
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
HLS_PROVIDER,
|
HLS_PROVIDER,
|
||||||
MAX_SEGMENTS,
|
MAX_SEGMENTS,
|
||||||
OUTPUT_IDLE_TIMEOUT,
|
OUTPUT_IDLE_TIMEOUT,
|
||||||
RECORDER_PROVIDER,
|
RECORDER_PROVIDER,
|
||||||
|
RTSP_TRANSPORTS,
|
||||||
SEGMENT_DURATION_ADJUSTER,
|
SEGMENT_DURATION_ADJUSTER,
|
||||||
STREAM_RESTART_INCREMENT,
|
STREAM_RESTART_INCREMENT,
|
||||||
STREAM_RESTART_RESET_TIME,
|
STREAM_RESTART_RESET_TIME,
|
||||||
@ -72,28 +75,32 @@ def redact_credentials(data: str) -> str:
|
|||||||
def create_stream(
|
def create_stream(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stream_source: str,
|
stream_source: str,
|
||||||
options: dict[str, str],
|
options: dict[str, Any],
|
||||||
stream_label: str | None = None,
|
stream_label: str | None = None,
|
||||||
) -> Stream:
|
) -> Stream:
|
||||||
"""Create a stream with the specified identfier based on the source url.
|
"""Create a stream with the specified identfier based on the source url.
|
||||||
|
|
||||||
The stream_source is typically an rtsp url (though any url accepted by ffmpeg is fine) and
|
The stream_source is typically an rtsp url (though any url accepted by ffmpeg is fine) and
|
||||||
options are passed into pyav / ffmpeg as options.
|
options (see STREAM_OPTIONS_SCHEMA) are converted and passed into pyav / ffmpeg.
|
||||||
|
|
||||||
The stream_label is a string used as an additional message in logging.
|
The stream_label is a string used as an additional message in logging.
|
||||||
"""
|
"""
|
||||||
if DOMAIN not in hass.config.components:
|
if DOMAIN not in hass.config.components:
|
||||||
raise HomeAssistantError("Stream integration is not set up.")
|
raise HomeAssistantError("Stream integration is not set up.")
|
||||||
|
|
||||||
|
# Convert extra stream options into PyAV options
|
||||||
|
pyav_options = convert_stream_options(options)
|
||||||
# For RTSP streams, prefer TCP
|
# For RTSP streams, prefer TCP
|
||||||
if isinstance(stream_source, str) and stream_source[:7] == "rtsp://":
|
if isinstance(stream_source, str) and stream_source[:7] == "rtsp://":
|
||||||
options = {
|
pyav_options = {
|
||||||
"rtsp_flags": "prefer_tcp",
|
"rtsp_flags": "prefer_tcp",
|
||||||
"stimeout": "5000000",
|
"stimeout": "5000000",
|
||||||
**options,
|
**pyav_options,
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = Stream(hass, stream_source, options=options, stream_label=stream_label)
|
stream = Stream(
|
||||||
|
hass, stream_source, options=pyav_options, stream_label=stream_label
|
||||||
|
)
|
||||||
hass.data[DOMAIN][ATTR_STREAMS].append(stream)
|
hass.data[DOMAIN][ATTR_STREAMS].append(stream)
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@ -464,3 +471,27 @@ class Stream:
|
|||||||
def _should_retry() -> bool:
|
def _should_retry() -> bool:
|
||||||
"""Return true if worker failures should be retried, for disabling during tests."""
|
"""Return true if worker failures should be retried, for disabling during tests."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
STREAM_OPTIONS_SCHEMA: Final = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS),
|
||||||
|
vol.Optional(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): bool,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_stream_options(stream_options: dict[str, Any]) -> dict[str, str]:
|
||||||
|
"""Convert options from stream options into PyAV options."""
|
||||||
|
pyav_options: dict[str, str] = {}
|
||||||
|
try:
|
||||||
|
STREAM_OPTIONS_SCHEMA(stream_options)
|
||||||
|
except vol.Invalid as exc:
|
||||||
|
raise HomeAssistantError("Invalid stream options") from exc
|
||||||
|
|
||||||
|
if rtsp_transport := stream_options.get(CONF_RTSP_TRANSPORT):
|
||||||
|
pyav_options["rtsp_transport"] = rtsp_transport
|
||||||
|
if stream_options.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
||||||
|
pyav_options["use_wallclock_as_timestamps"] = "1"
|
||||||
|
|
||||||
|
return pyav_options
|
||||||
|
@ -42,3 +42,14 @@ STREAM_RESTART_RESET_TIME = 300 # Reset wait_timeout after this many seconds
|
|||||||
CONF_LL_HLS = "ll_hls"
|
CONF_LL_HLS = "ll_hls"
|
||||||
CONF_PART_DURATION = "part_duration"
|
CONF_PART_DURATION = "part_duration"
|
||||||
CONF_SEGMENT_DURATION = "segment_duration"
|
CONF_SEGMENT_DURATION = "segment_duration"
|
||||||
|
|
||||||
|
CONF_PREFER_TCP = "prefer_tcp"
|
||||||
|
CONF_RTSP_TRANSPORT = "rtsp_transport"
|
||||||
|
# The first dict entry below may be used as the default when populating options
|
||||||
|
RTSP_TRANSPORTS = {
|
||||||
|
"tcp": "TCP",
|
||||||
|
"udp": "UDP",
|
||||||
|
"udp_multicast": "UDP Multicast",
|
||||||
|
"http": "HTTP",
|
||||||
|
}
|
||||||
|
CONF_USE_WALLCLOCK_AS_TIMESTAMPS = "use_wallclock_as_timestamps"
|
||||||
|
@ -452,6 +452,10 @@ def stream_worker(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Handle consuming streams."""
|
"""Handle consuming streams."""
|
||||||
|
|
||||||
|
if av.library_versions["libavformat"][0] >= 59 and "stimeout" in options:
|
||||||
|
# the stimeout option was renamed to timeout as of ffmpeg 5.0
|
||||||
|
options["timeout"] = options["stimeout"]
|
||||||
|
del options["stimeout"]
|
||||||
try:
|
try:
|
||||||
container = av.open(source, options=options, timeout=SOURCE_TIMEOUT)
|
container = av.open(source, options=options, timeout=SOURCE_TIMEOUT)
|
||||||
except av.AVError as err:
|
except av.AVError as err:
|
||||||
|
@ -15,12 +15,14 @@ from homeassistant.components.generic.const import (
|
|||||||
CONF_CONTENT_TYPE,
|
CONF_CONTENT_TYPE,
|
||||||
CONF_FRAMERATE,
|
CONF_FRAMERATE,
|
||||||
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
CONF_LIMIT_REFETCH_TO_URL_CHANGE,
|
||||||
CONF_RTSP_TRANSPORT,
|
|
||||||
CONF_STILL_IMAGE_URL,
|
CONF_STILL_IMAGE_URL,
|
||||||
CONF_STREAM_SOURCE,
|
CONF_STREAM_SOURCE,
|
||||||
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.stream.const import (
|
||||||
|
CONF_RTSP_TRANSPORT,
|
||||||
|
CONF_USE_WALLCLOCK_AS_TIMESTAMPS,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_AUTHENTICATION,
|
CONF_AUTHENTICATION,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
@ -323,12 +323,12 @@ async def test_option_flow(hass):
|
|||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={
|
user_input={
|
||||||
config_flow.CONF_EXTRA_ARGUMENTS: "",
|
config_flow.CONF_EXTRA_ARGUMENTS: "",
|
||||||
config_flow.CONF_RTSP_TRANSPORT: config_flow.RTSP_TRANS_PROTOCOLS[1],
|
config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
assert result["data"] == {
|
assert result["data"] == {
|
||||||
config_flow.CONF_EXTRA_ARGUMENTS: "",
|
config_flow.CONF_EXTRA_ARGUMENTS: "",
|
||||||
config_flow.CONF_RTSP_TRANSPORT: config_flow.RTSP_TRANS_PROTOCOLS[1],
|
config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1],
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user