Add Homekit cameras codecs (#35238)
* Homekit cameras - Add codecs support * Add valid_codecs + move audio application parameter * Increase video bufsize * Increase audio bufsize * Update config flow to be aware of the copy option * Add tests for copy video and audio codec * remove unused from test * remove unused from test Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
5e33842ce0
commit
a38bb5b33b
9 changed files with 377 additions and 149 deletions
|
@ -186,6 +186,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||
# If the previous instance hasn't cleaned up yet
|
||||
# we need to wait a bit
|
||||
if not await hass.async_add_executor_job(port_is_available, port):
|
||||
_LOGGER.warning("The local port %s is in use.", port)
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
if CONF_ENTRY_INDEX in conf and conf[CONF_ENTRY_INDEX] == 0:
|
||||
|
@ -202,8 +203,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||
# These are yaml only
|
||||
ip_address = conf.get(CONF_IP_ADDRESS)
|
||||
advertise_ip = conf.get(CONF_ADVERTISE_IP)
|
||||
entity_config = conf.get(CONF_ENTITY_CONFIG, {})
|
||||
|
||||
entity_config = options.get(CONF_ENTITY_CONFIG, {}).copy()
|
||||
auto_start = options.get(CONF_AUTO_START, DEFAULT_AUTO_START)
|
||||
safe_mode = options.get(CONF_SAFE_MODE, DEFAULT_SAFE_MODE)
|
||||
entity_filter = convert_filter(
|
||||
|
|
|
@ -19,21 +19,26 @@ from homeassistant.helpers.entityfilter import (
|
|||
|
||||
from .const import (
|
||||
CONF_AUTO_START,
|
||||
CONF_ENTITY_CONFIG,
|
||||
CONF_FILTER,
|
||||
CONF_SAFE_MODE,
|
||||
CONF_VIDEO_CODEC,
|
||||
CONF_ZEROCONF_DEFAULT_INTERFACE,
|
||||
DEFAULT_AUTO_START,
|
||||
DEFAULT_CONFIG_FLOW_PORT,
|
||||
DEFAULT_SAFE_MODE,
|
||||
DEFAULT_ZEROCONF_DEFAULT_INTERFACE,
|
||||
SHORT_BRIDGE_NAME,
|
||||
VIDEO_CODEC_COPY,
|
||||
)
|
||||
from .const import DOMAIN # pylint:disable=unused-import
|
||||
from .util import find_next_available_port
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_CAMERA_COPY = "camera_copy"
|
||||
CONF_DOMAINS = "domains"
|
||||
|
||||
SUPPORTED_DOMAINS = [
|
||||
"alarm_control_panel",
|
||||
"automation",
|
||||
|
@ -183,6 +188,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||
"""Initialize options flow."""
|
||||
self.config_entry = config_entry
|
||||
self.homekit_options = {}
|
||||
self.included_cameras = set()
|
||||
|
||||
async def async_step_yaml(self, user_input=None):
|
||||
"""No options for yaml managed entries."""
|
||||
|
@ -236,6 +242,38 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||
step_id="advanced", data_schema=vol.Schema(schema_base)
|
||||
)
|
||||
|
||||
async def async_step_cameras(self, user_input=None):
|
||||
"""Choose camera config."""
|
||||
if user_input is not None:
|
||||
entity_config = self.homekit_options[CONF_ENTITY_CONFIG]
|
||||
for entity_id in self.included_cameras:
|
||||
if entity_id in user_input[CONF_CAMERA_COPY]:
|
||||
entity_config.setdefault(entity_id, {})[
|
||||
CONF_VIDEO_CODEC
|
||||
] = VIDEO_CODEC_COPY
|
||||
elif (
|
||||
entity_id in entity_config
|
||||
and CONF_VIDEO_CODEC in entity_config[entity_id]
|
||||
):
|
||||
del entity_config[entity_id][CONF_VIDEO_CODEC]
|
||||
return await self.async_step_advanced()
|
||||
|
||||
cameras_with_copy = []
|
||||
entity_config = self.homekit_options.setdefault(CONF_ENTITY_CONFIG, {})
|
||||
for entity in self.included_cameras:
|
||||
hk_entity_config = entity_config.get(entity, {})
|
||||
if hk_entity_config.get(CONF_VIDEO_CODEC) == VIDEO_CODEC_COPY:
|
||||
cameras_with_copy.append(entity)
|
||||
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_CAMERA_COPY, default=cameras_with_copy,
|
||||
): cv.multi_select(self.included_cameras),
|
||||
}
|
||||
)
|
||||
return self.async_show_form(step_id="cameras", data_schema=data_schema)
|
||||
|
||||
async def async_step_exclude(self, user_input=None):
|
||||
"""Choose entities to exclude from the domain."""
|
||||
if user_input is not None:
|
||||
|
@ -249,6 +287,11 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||
),
|
||||
CONF_EXCLUDE_ENTITIES: user_input[CONF_EXCLUDE_ENTITIES],
|
||||
}
|
||||
for entity_id in user_input[CONF_EXCLUDE_ENTITIES]:
|
||||
if entity_id in self.included_cameras:
|
||||
self.included_cameras.remove(entity_id)
|
||||
if self.included_cameras:
|
||||
return await self.async_step_cameras()
|
||||
return await self.async_step_advanced()
|
||||
|
||||
entity_filter = self.homekit_options.get(CONF_FILTER, {})
|
||||
|
@ -257,6 +300,11 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||
self.hass,
|
||||
self.homekit_options[CONF_INCLUDE_DOMAINS],
|
||||
)
|
||||
self.included_cameras = {
|
||||
entity_id
|
||||
for entity_id in all_supported_entities
|
||||
if entity_id.startswith("camera.")
|
||||
}
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
|
|
|
@ -13,12 +13,20 @@ UNDO_UPDATE_LISTENER = "undo_update_listener"
|
|||
SHUTDOWN_TIMEOUT = 30
|
||||
CONF_ENTRY_INDEX = "index"
|
||||
|
||||
# ### Codecs ####
|
||||
VIDEO_CODEC_COPY = "copy"
|
||||
VIDEO_CODEC_LIBX264 = "libx264"
|
||||
AUDIO_CODEC_OPUS = "libopus"
|
||||
VIDEO_CODEC_H264_OMX = "h264_omx"
|
||||
AUDIO_CODEC_COPY = "copy"
|
||||
|
||||
# #### Attributes ####
|
||||
ATTR_DISPLAY_NAME = "display_name"
|
||||
ATTR_VALUE = "value"
|
||||
|
||||
# #### Config ####
|
||||
CONF_ADVERTISE_IP = "advertise_ip"
|
||||
CONF_AUDIO_CODEC = "audio_codec"
|
||||
CONF_AUDIO_MAP = "audio_map"
|
||||
CONF_AUDIO_PACKET_SIZE = "audio_packet_size"
|
||||
CONF_AUTO_START = "auto_start"
|
||||
|
@ -37,10 +45,12 @@ CONF_ZEROCONF_DEFAULT_INTERFACE = "zeroconf_default_interface"
|
|||
CONF_STREAM_ADDRESS = "stream_address"
|
||||
CONF_STREAM_SOURCE = "stream_source"
|
||||
CONF_SUPPORT_AUDIO = "support_audio"
|
||||
CONF_VIDEO_CODEC = "video_codec"
|
||||
CONF_VIDEO_MAP = "video_map"
|
||||
CONF_VIDEO_PACKET_SIZE = "video_packet_size"
|
||||
|
||||
# #### Config Defaults ####
|
||||
DEFAULT_AUDIO_CODEC = AUDIO_CODEC_OPUS
|
||||
DEFAULT_AUDIO_MAP = "0:a:0"
|
||||
DEFAULT_AUDIO_PACKET_SIZE = 188
|
||||
DEFAULT_AUTO_START = True
|
||||
|
@ -52,6 +62,7 @@ DEFAULT_PORT = 51827
|
|||
DEFAULT_CONFIG_FLOW_PORT = 51828
|
||||
DEFAULT_SAFE_MODE = False
|
||||
DEFAULT_ZEROCONF_DEFAULT_INTERFACE = False
|
||||
DEFAULT_VIDEO_CODEC = VIDEO_CODEC_LIBX264
|
||||
DEFAULT_VIDEO_MAP = "0:v:0"
|
||||
DEFAULT_VIDEO_PACKET_SIZE = 1316
|
||||
|
||||
|
@ -233,4 +244,5 @@ CONFIG_OPTIONS = [
|
|||
CONF_AUTO_START,
|
||||
CONF_ZEROCONF_DEFAULT_INTERFACE,
|
||||
CONF_SAFE_MODE,
|
||||
CONF_ENTITY_CONFIG,
|
||||
]
|
||||
|
|
|
@ -20,6 +20,13 @@
|
|||
"description": "Choose the entities that you do NOT want to be bridged.",
|
||||
"title": "Exclude entities in selected domains from bridge"
|
||||
},
|
||||
"cameras": {
|
||||
"data": {
|
||||
"camera_copy": "Cameras that support native H.264 streams"
|
||||
},
|
||||
"description": "Check all cameras that support native H.264 streams. If the camera does not output a H.264 stream, the system will transcode the video to H.264 for HomeKit. Transcoding requires a performant CPU and is unlikely to work on single board computers.",
|
||||
"title": "Select camera video codec."
|
||||
},
|
||||
"advanced": {
|
||||
"data": {
|
||||
"auto_start": "[%key:component::homekit::config::step::user::data::auto_start%]",
|
||||
|
|
|
@ -1,33 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"port_name_in_use": "A bridge with the same name or port is already configured."
|
||||
},
|
||||
"step": {
|
||||
"pairing": {
|
||||
"description": "As soon as the {name} bridge is ready, pairing will be available in \u201cNotifications\u201d as \u201cHomeKit Bridge Setup\u201d.",
|
||||
"title": "Pair HomeKit Bridge"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"auto_start": "Autostart (disable if using Z-Wave or other delayed start system)",
|
||||
"include_domains": "Domains to include"
|
||||
},
|
||||
"description": "A HomeKit Bridge will allow you to access your Home Assistant entities in HomeKit. HomeKit Bridges are limited to 150 accessories per instance including the bridge itself. If you wish to bridge more than the maximum number of accessories, it is recommended that you use multiple HomeKit bridges for different domains. Detailed entity configuration is only available via YAML for the primary bridge.",
|
||||
"title": "Activate HomeKit Bridge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "HomeKit Bridge",
|
||||
"options": {
|
||||
"step": {
|
||||
"advanced": {
|
||||
"yaml": {
|
||||
"title": "Adjust HomeKit Bridge Options",
|
||||
"description": "This entry is controlled via YAML"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"auto_start": "Autostart (disable if using Z-Wave or other delayed start system)",
|
||||
"safe_mode": "Safe Mode (enable only if pairing fails)",
|
||||
"zeroconf_default_interface": "Use default zeroconf interface (enable if the bridge cannot be found in the Home app)"
|
||||
"include_domains": "[%key:component::homekit::config::step::user::data::include_domains%]"
|
||||
},
|
||||
"description": "These settings only need to be adjusted if the HomeKit bridge is not functional.",
|
||||
"title": "Advanced Configuration"
|
||||
"description": "Entities in the \u201cDomains to include\u201d will be bridged to HomeKit. You will be able to select which entities to exclude from this list on the next screen.",
|
||||
"title": "Select domains to bridge."
|
||||
},
|
||||
"exclude": {
|
||||
"data": {
|
||||
|
@ -36,18 +20,41 @@
|
|||
"description": "Choose the entities that you do NOT want to be bridged.",
|
||||
"title": "Exclude entities in selected domains from bridge"
|
||||
},
|
||||
"init": {
|
||||
"cameras": {
|
||||
"data": {
|
||||
"include_domains": "Domains to include"
|
||||
"camera_copy": "Cameras that support native H.264 streams"
|
||||
},
|
||||
"description": "Entities in the \u201cDomains to include\u201d will be bridged to HomeKit. You will be able to select which entities to exclude from this list on the next screen.",
|
||||
"title": "Select domains to bridge."
|
||||
"description": "Check all cameras that support native H.264 streams. If the camera does not output a H.264 stream, the system will transcode the video to H.264 for HomeKit. Transcoding requires a performant CPU and is unlikely to work on single board computers.",
|
||||
"title": "Select camera video codec."
|
||||
},
|
||||
"yaml": {
|
||||
"description": "This entry is controlled via YAML",
|
||||
"title": "Adjust HomeKit Bridge Options"
|
||||
"advanced": {
|
||||
"data": {
|
||||
"auto_start": "[%key:component::homekit::config::step::user::data::auto_start%]",
|
||||
"safe_mode": "Safe Mode (enable only if pairing fails)",
|
||||
"zeroconf_default_interface": "Use default zeroconf interface (enable if the bridge cannot be found in the Home app)"
|
||||
},
|
||||
"description": "These settings only need to be adjusted if the HomeKit bridge is not functional.",
|
||||
"title": "Advanced Configuration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "HomeKit Bridge"
|
||||
}
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"auto_start": "Autostart (disable if using Z-Wave or other delayed start system)",
|
||||
"include_domains": "Domains to include"
|
||||
},
|
||||
"description": "A HomeKit Bridge will allow you to access your Home Assistant entities in HomeKit. HomeKit Bridges are limited to 150 accessories per instance including the bridge itself. If you wish to bridge more than the maximum number of accessories, it is recommended that you use multiple HomeKit bridges for different domains. Detailed entity configuration is only available via YAML for the primary bridge.",
|
||||
"title": "Activate HomeKit Bridge"
|
||||
},
|
||||
"pairing": {
|
||||
"title": "Pair HomeKit Bridge",
|
||||
"description": "As soon as the {name} bridge is ready, pairing will be available in \u201cNotifications\u201d as \u201cHomeKit Bridge Setup\u201d."
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"port_name_in_use": "A bridge with the same name or port is already configured."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ from homeassistant.util import get_local_ip
|
|||
|
||||
from .accessories import TYPES, HomeAccessory
|
||||
from .const import (
|
||||
CONF_AUDIO_CODEC,
|
||||
CONF_AUDIO_MAP,
|
||||
CONF_AUDIO_PACKET_SIZE,
|
||||
CONF_MAX_FPS,
|
||||
|
@ -24,6 +25,7 @@ from .const import (
|
|||
CONF_STREAM_ADDRESS,
|
||||
CONF_STREAM_SOURCE,
|
||||
CONF_SUPPORT_AUDIO,
|
||||
CONF_VIDEO_CODEC,
|
||||
CONF_VIDEO_MAP,
|
||||
CONF_VIDEO_PACKET_SIZE,
|
||||
)
|
||||
|
@ -33,7 +35,9 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
VIDEO_OUTPUT = (
|
||||
"-map {v_map} -an "
|
||||
"-c:v libx264 -profile:v {v_profile} -tune zerolatency -pix_fmt yuv420p "
|
||||
"-c:v {v_codec} "
|
||||
"{v_profile}"
|
||||
"-tune zerolatency -pix_fmt yuv420p "
|
||||
"-r {fps} "
|
||||
"-b:v {v_max_bitrate}k -bufsize {v_bufsize}k -maxrate {v_max_bitrate}k "
|
||||
"-payload_type 99 "
|
||||
|
@ -43,11 +47,10 @@ VIDEO_OUTPUT = (
|
|||
"localrtcpport={v_port}&pkt_size={v_pkt_size}"
|
||||
)
|
||||
|
||||
AUDIO_ENCODER_OPUS = "libopus -application lowdelay"
|
||||
|
||||
AUDIO_OUTPUT = (
|
||||
"-map {a_map} -vn "
|
||||
"-c:a {a_encoder} "
|
||||
"{a_application}"
|
||||
"-ac 1 -ar {a_sample_rate}k "
|
||||
"-b:a {a_max_bitrate}k -bufsize {a_bufsize}k "
|
||||
"-payload_type 110 "
|
||||
|
@ -171,19 +174,31 @@ class Camera(HomeAccessory, PyhapCamera):
|
|||
return False
|
||||
if "-i " not in input_source:
|
||||
input_source = "-i " + input_source
|
||||
video_profile = ""
|
||||
if self.config[CONF_VIDEO_CODEC] != "copy":
|
||||
video_profile = (
|
||||
"-profile:v "
|
||||
+ VIDEO_PROFILE_NAMES[
|
||||
int.from_bytes(stream_config["v_profile_id"], byteorder="big")
|
||||
]
|
||||
+ " "
|
||||
)
|
||||
audio_application = ""
|
||||
if self.config[CONF_AUDIO_CODEC] == "libopus":
|
||||
audio_application = "-application lowdelay "
|
||||
output_vars = stream_config.copy()
|
||||
output_vars.update(
|
||||
{
|
||||
"v_profile": VIDEO_PROFILE_NAMES[
|
||||
int.from_bytes(stream_config["v_profile_id"], byteorder="big")
|
||||
],
|
||||
"v_bufsize": stream_config["v_max_bitrate"] * 2,
|
||||
"v_profile": video_profile,
|
||||
"v_bufsize": stream_config["v_max_bitrate"] * 4,
|
||||
"v_map": self.config[CONF_VIDEO_MAP],
|
||||
"v_pkt_size": self.config[CONF_VIDEO_PACKET_SIZE],
|
||||
"a_bufsize": stream_config["a_max_bitrate"] * 2,
|
||||
"v_codec": self.config[CONF_VIDEO_CODEC],
|
||||
"a_bufsize": stream_config["a_max_bitrate"] * 4,
|
||||
"a_map": self.config[CONF_AUDIO_MAP],
|
||||
"a_pkt_size": self.config[CONF_AUDIO_PACKET_SIZE],
|
||||
"a_encoder": AUDIO_ENCODER_OPUS,
|
||||
"a_encoder": self.config[CONF_AUDIO_CODEC],
|
||||
"a_application": audio_application,
|
||||
}
|
||||
)
|
||||
output = VIDEO_OUTPUT.format(**output_vars)
|
||||
|
|
|
@ -24,6 +24,9 @@ from homeassistant.helpers.storage import STORAGE_DIR
|
|||
import homeassistant.util.temperature as temp_util
|
||||
|
||||
from .const import (
|
||||
AUDIO_CODEC_COPY,
|
||||
AUDIO_CODEC_OPUS,
|
||||
CONF_AUDIO_CODEC,
|
||||
CONF_AUDIO_MAP,
|
||||
CONF_AUDIO_PACKET_SIZE,
|
||||
CONF_FEATURE,
|
||||
|
@ -36,14 +39,17 @@ from .const import (
|
|||
CONF_STREAM_ADDRESS,
|
||||
CONF_STREAM_SOURCE,
|
||||
CONF_SUPPORT_AUDIO,
|
||||
CONF_VIDEO_CODEC,
|
||||
CONF_VIDEO_MAP,
|
||||
CONF_VIDEO_PACKET_SIZE,
|
||||
DEFAULT_AUDIO_CODEC,
|
||||
DEFAULT_AUDIO_MAP,
|
||||
DEFAULT_AUDIO_PACKET_SIZE,
|
||||
DEFAULT_LOW_BATTERY_THRESHOLD,
|
||||
DEFAULT_MAX_FPS,
|
||||
DEFAULT_MAX_HEIGHT,
|
||||
DEFAULT_MAX_WIDTH,
|
||||
DEFAULT_VIDEO_CODEC,
|
||||
DEFAULT_VIDEO_MAP,
|
||||
DEFAULT_VIDEO_PACKET_SIZE,
|
||||
DOMAIN,
|
||||
|
@ -60,11 +66,16 @@ from .const import (
|
|||
TYPE_SPRINKLER,
|
||||
TYPE_SWITCH,
|
||||
TYPE_VALVE,
|
||||
VIDEO_CODEC_COPY,
|
||||
VIDEO_CODEC_H264_OMX,
|
||||
VIDEO_CODEC_LIBX264,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MAX_PORT = 65535
|
||||
VALID_VIDEO_CODECS = [VIDEO_CODEC_LIBX264, VIDEO_CODEC_H264_OMX, AUDIO_CODEC_COPY]
|
||||
VALID_AUDIO_CODECS = [AUDIO_CODEC_OPUS, VIDEO_CODEC_COPY]
|
||||
|
||||
BASIC_INFO_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -84,12 +95,18 @@ CAMERA_SCHEMA = BASIC_INFO_SCHEMA.extend(
|
|||
{
|
||||
vol.Optional(CONF_STREAM_ADDRESS): vol.All(ipaddress.ip_address, cv.string),
|
||||
vol.Optional(CONF_STREAM_SOURCE): cv.string,
|
||||
vol.Optional(CONF_AUDIO_CODEC, default=DEFAULT_AUDIO_CODEC): vol.In(
|
||||
VALID_AUDIO_CODECS
|
||||
),
|
||||
vol.Optional(CONF_SUPPORT_AUDIO, default=False): cv.boolean,
|
||||
vol.Optional(CONF_MAX_WIDTH, default=DEFAULT_MAX_WIDTH): cv.positive_int,
|
||||
vol.Optional(CONF_MAX_HEIGHT, default=DEFAULT_MAX_HEIGHT): cv.positive_int,
|
||||
vol.Optional(CONF_MAX_FPS, default=DEFAULT_MAX_FPS): cv.positive_int,
|
||||
vol.Optional(CONF_AUDIO_MAP, default=DEFAULT_AUDIO_MAP): cv.string,
|
||||
vol.Optional(CONF_VIDEO_MAP, default=DEFAULT_VIDEO_MAP): cv.string,
|
||||
vol.Optional(CONF_VIDEO_CODEC, default=DEFAULT_VIDEO_CODEC): vol.In(
|
||||
VALID_VIDEO_CODECS
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_AUDIO_PACKET_SIZE, default=DEFAULT_AUDIO_PACKET_SIZE
|
||||
): cv.positive_int,
|
||||
|
|
|
@ -220,6 +220,120 @@ async def test_options_flow_basic(hass):
|
|||
}
|
||||
|
||||
|
||||
async def test_options_flow_with_cameras(hass):
|
||||
"""Test config flow options."""
|
||||
|
||||
config_entry = _mock_config_entry_with_options_populated()
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
hass.states.async_set("climate.old", "off")
|
||||
hass.states.async_set("camera.native_h264", "off")
|
||||
hass.states.async_set("camera.transcode_h264", "off")
|
||||
hass.states.async_set("camera.excluded", "off")
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
config_entry.entry_id, context={"show_advanced_options": False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"include_domains": ["fan", "vacuum", "climate", "camera"]},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "exclude"
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"exclude_entities": ["climate.old", "camera.excluded"]},
|
||||
)
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result2["step_id"] == "cameras"
|
||||
|
||||
result3 = await hass.config_entries.options.async_configure(
|
||||
result2["flow_id"], user_input={"camera_copy": ["camera.native_h264"]},
|
||||
)
|
||||
|
||||
assert result3["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result3["step_id"] == "advanced"
|
||||
|
||||
with patch("homeassistant.components.homekit.async_setup_entry", return_value=True):
|
||||
result4 = await hass.config_entries.options.async_configure(
|
||||
result3["flow_id"],
|
||||
user_input={"safe_mode": True, "zeroconf_default_interface": False},
|
||||
)
|
||||
|
||||
assert result4["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options == {
|
||||
"auto_start": False,
|
||||
"filter": {
|
||||
"exclude_domains": [],
|
||||
"exclude_entities": ["climate.old", "camera.excluded"],
|
||||
"include_domains": ["fan", "vacuum", "climate", "camera"],
|
||||
"include_entities": [],
|
||||
},
|
||||
"entity_config": {"camera.native_h264": {"video_codec": "copy"}},
|
||||
"safe_mode": True,
|
||||
"zeroconf_default_interface": False,
|
||||
}
|
||||
|
||||
# Now run though again and verify we can turn off copy
|
||||
|
||||
result = await hass.config_entries.options.async_init(
|
||||
config_entry.entry_id, context={"show_advanced_options": False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"include_domains": ["fan", "vacuum", "climate", "camera"]},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "exclude"
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={"exclude_entities": ["climate.old", "camera.excluded"]},
|
||||
)
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result2["step_id"] == "cameras"
|
||||
|
||||
result3 = await hass.config_entries.options.async_configure(
|
||||
result2["flow_id"], user_input={"camera_copy": []},
|
||||
)
|
||||
|
||||
assert result3["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result3["step_id"] == "advanced"
|
||||
|
||||
with patch("homeassistant.components.homekit.async_setup_entry", return_value=True):
|
||||
result4 = await hass.config_entries.options.async_configure(
|
||||
result3["flow_id"],
|
||||
user_input={"safe_mode": True, "zeroconf_default_interface": False},
|
||||
)
|
||||
|
||||
assert result4["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options == {
|
||||
"auto_start": False,
|
||||
"filter": {
|
||||
"exclude_domains": [],
|
||||
"exclude_entities": ["climate.old", "camera.excluded"],
|
||||
"include_domains": ["fan", "vacuum", "climate", "camera"],
|
||||
"include_entities": [],
|
||||
},
|
||||
"entity_config": {"camera.native_h264": {}},
|
||||
"safe_mode": True,
|
||||
"zeroconf_default_interface": False,
|
||||
}
|
||||
|
||||
|
||||
async def test_options_flow_blocked_when_from_yaml(hass):
|
||||
"""Test config flow options."""
|
||||
|
||||
|
|
|
@ -8,8 +8,12 @@ import pytest
|
|||
from homeassistant.components import camera, ffmpeg
|
||||
from homeassistant.components.homekit.accessories import HomeBridge
|
||||
from homeassistant.components.homekit.const import (
|
||||
AUDIO_CODEC_COPY,
|
||||
CONF_AUDIO_CODEC,
|
||||
CONF_STREAM_SOURCE,
|
||||
CONF_SUPPORT_AUDIO,
|
||||
CONF_VIDEO_CODEC,
|
||||
VIDEO_CODEC_COPY,
|
||||
)
|
||||
from homeassistant.components.homekit.type_cameras import Camera
|
||||
from homeassistant.components.homekit.type_switches import Switch
|
||||
|
@ -18,6 +22,10 @@ from homeassistant.setup import async_setup_component
|
|||
|
||||
from tests.async_mock import AsyncMock, MagicMock, patch
|
||||
|
||||
MOCK_START_STREAM_TLV = "ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
MOCK_END_POINTS_TLV = "ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
MOCK_START_STREAM_SESSION_UUID = UUID("3303d503-17cc-469a-b672-92436a71a2f6")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def run_driver(hass):
|
||||
|
@ -81,49 +89,47 @@ async def test_camera_stream_source_configured(hass, run_driver, events):
|
|||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
stream_service = acc.get_service("CameraRTPStreamManagement")
|
||||
endpoints_config_char = stream_service.get_characteristic("SetupEndpoints")
|
||||
assert endpoints_config_char.setter_callback
|
||||
stream_config_char = stream_service.get_characteristic(
|
||||
"SelectedRTPStreamConfiguration"
|
||||
)
|
||||
assert stream_config_char.setter_callback
|
||||
acc.set_endpoints(
|
||||
"ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
)
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
session_info = acc.sessions[MOCK_START_STREAM_SESSION_UUID]
|
||||
working_ffmpeg = _get_working_mock_ffmpeg()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
return_value=None,
|
||||
), patch(
|
||||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_working_mock_ffmpeg(),
|
||||
return_value=working_ffmpeg,
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
session_info = {
|
||||
"id": "mock",
|
||||
"v_srtp_key": "key",
|
||||
"a_srtp_key": "key",
|
||||
"v_port": "0",
|
||||
"a_port": "0",
|
||||
"address": "0.0.0.0",
|
||||
}
|
||||
acc.sessions[UUID("3303d503-17cc-469a-b672-92436a71a2f6")] = session_info
|
||||
expected_output = (
|
||||
"-map 0:v:0 -an -c:v libx264 -profile:v high -tune zerolatency -pix_fmt "
|
||||
"yuv420p -r 30 -b:v 299k -bufsize 1196k -maxrate 299k -payload_type 99 -ssrc {v_ssrc} -f "
|
||||
"rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params "
|
||||
"zdPmNLWeI86DtLJHvVLI6YPvqhVeeiLsNtrAgbgL "
|
||||
"srtp://192.168.208.5:51246?rtcpport=51246&localrtcpport=51246&pkt_size=1316 -map 0:a:0 "
|
||||
"-vn -c:a libopus -application lowdelay -ac 1 -ar 24k -b:a 24k -bufsize 96k -payload_type "
|
||||
"110 -ssrc {a_ssrc} -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params "
|
||||
"shnETgfD+7xUQ8zRdsaytY11wu6CO73IJ+RZVJpU "
|
||||
"srtp://192.168.208.5:51108?rtcpport=51108&localrtcpport=51108&pkt_size=188"
|
||||
)
|
||||
|
||||
working_ffmpeg.open.assert_called_with(
|
||||
cmd=[],
|
||||
input_source="-i /dev/null",
|
||||
output=expected_output.format(**session_info),
|
||||
stdout_pipe=False,
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
return_value="rtsp://example.local",
|
||||
), patch(
|
||||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_working_mock_ffmpeg(),
|
||||
return_value=working_ffmpeg,
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
# Calling a second time should not throw
|
||||
await acc.stop_stream(session_info)
|
||||
|
@ -171,26 +177,8 @@ async def test_camera_stream_source_configured_with_failing_ffmpeg(
|
|||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
stream_service = acc.get_service("CameraRTPStreamManagement")
|
||||
endpoints_config_char = stream_service.get_characteristic("SetupEndpoints")
|
||||
assert endpoints_config_char.setter_callback
|
||||
stream_config_char = stream_service.get_characteristic(
|
||||
"SelectedRTPStreamConfiguration"
|
||||
)
|
||||
assert stream_config_char.setter_callback
|
||||
acc.set_endpoints(
|
||||
"ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
)
|
||||
|
||||
session_info = {
|
||||
"id": "mock",
|
||||
"v_srtp_key": "key",
|
||||
"a_srtp_key": "key",
|
||||
"v_port": "0",
|
||||
"a_port": "0",
|
||||
"address": "0.0.0.0",
|
||||
}
|
||||
acc.sessions[UUID("3303d503-17cc-469a-b672-92436a71a2f6")] = session_info
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
session_info = acc.sessions[MOCK_START_STREAM_SESSION_UUID]
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
|
@ -199,9 +187,7 @@ async def test_camera_stream_source_configured_with_failing_ffmpeg(
|
|||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_failing_mock_ffmpeg(),
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
# Calling a second time should not throw
|
||||
await acc.stop_stream(session_info)
|
||||
|
@ -225,26 +211,8 @@ async def test_camera_stream_source_found(hass, run_driver, events):
|
|||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
stream_service = acc.get_service("CameraRTPStreamManagement")
|
||||
endpoints_config_char = stream_service.get_characteristic("SetupEndpoints")
|
||||
assert endpoints_config_char.setter_callback
|
||||
stream_config_char = stream_service.get_characteristic(
|
||||
"SelectedRTPStreamConfiguration"
|
||||
)
|
||||
assert stream_config_char.setter_callback
|
||||
acc.set_endpoints(
|
||||
"ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
)
|
||||
|
||||
session_info = {
|
||||
"id": "mock",
|
||||
"v_srtp_key": "key",
|
||||
"a_srtp_key": "key",
|
||||
"v_port": "0",
|
||||
"a_port": "0",
|
||||
"address": "0.0.0.0",
|
||||
}
|
||||
acc.sessions[UUID("3303d503-17cc-469a-b672-92436a71a2f6")] = session_info
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
session_info = acc.sessions[MOCK_START_STREAM_SESSION_UUID]
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
|
@ -253,9 +221,7 @@ async def test_camera_stream_source_found(hass, run_driver, events):
|
|||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_working_mock_ffmpeg(),
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -266,9 +232,7 @@ async def test_camera_stream_source_found(hass, run_driver, events):
|
|||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_working_mock_ffmpeg(),
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -290,26 +254,8 @@ async def test_camera_stream_source_fails(hass, run_driver, events):
|
|||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
stream_service = acc.get_service("CameraRTPStreamManagement")
|
||||
endpoints_config_char = stream_service.get_characteristic("SetupEndpoints")
|
||||
assert endpoints_config_char.setter_callback
|
||||
stream_config_char = stream_service.get_characteristic(
|
||||
"SelectedRTPStreamConfiguration"
|
||||
)
|
||||
assert stream_config_char.setter_callback
|
||||
acc.set_endpoints(
|
||||
"ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
)
|
||||
|
||||
session_info = {
|
||||
"id": "mock",
|
||||
"v_srtp_key": "key",
|
||||
"a_srtp_key": "key",
|
||||
"v_port": "0",
|
||||
"a_port": "0",
|
||||
"address": "0.0.0.0",
|
||||
}
|
||||
acc.sessions[UUID("3303d503-17cc-469a-b672-92436a71a2f6")] = session_info
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
session_info = acc.sessions[MOCK_START_STREAM_SESSION_UUID]
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
|
@ -318,9 +264,7 @@ async def test_camera_stream_source_fails(hass, run_driver, events):
|
|||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=_get_working_mock_ffmpeg(),
|
||||
):
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -340,13 +284,76 @@ async def test_camera_with_no_stream(hass, run_driver, events):
|
|||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
acc.set_endpoints(
|
||||
"ARAzA9UDF8xGmrZykkNqcaL2AgEAAxoBAQACDTE5Mi4xNjguMjA4LjUDAi7IBAKkxwQlAQEAAhDN0+Y0tZ4jzoO0ske9UsjpAw6D76oVXnoi7DbawIG4CwUlAQEAAhCyGcROB8P7vFRDzNF2xrK1Aw6NdcLugju9yCfkWVSaVAYEDoAsAAcEpxV8AA=="
|
||||
)
|
||||
acc.set_selected_stream_configuration(
|
||||
"ARUCAQEBEDMD1QMXzEaatnKSQ2pxovYCNAEBAAIJAQECAgECAwEAAwsBAgAFAgLQAgMBHgQXAQFjAgQ768/RAwIrAQQEAAAAPwUCYgUDLAEBAwIMAQEBAgEAAwECBAEUAxYBAW4CBCzq28sDAhgABAQAAKBABgENBAEA"
|
||||
)
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.async_add_executor_job(acc.get_snapshot, 1024)
|
||||
|
||||
|
||||
async def test_camera_stream_source_configured_and_copy_codec(hass, run_driver, events):
|
||||
"""Test a camera that can stream with a configured source."""
|
||||
await async_setup_component(hass, ffmpeg.DOMAIN, {ffmpeg.DOMAIN: {}})
|
||||
await async_setup_component(
|
||||
hass, camera.DOMAIN, {camera.DOMAIN: {"platform": "demo"}}
|
||||
)
|
||||
|
||||
entity_id = "camera.demo_camera"
|
||||
|
||||
hass.states.async_set(entity_id, None)
|
||||
await hass.async_block_till_done()
|
||||
acc = Camera(
|
||||
hass,
|
||||
run_driver,
|
||||
"Camera",
|
||||
entity_id,
|
||||
2,
|
||||
{
|
||||
CONF_STREAM_SOURCE: "/dev/null",
|
||||
CONF_SUPPORT_AUDIO: True,
|
||||
CONF_VIDEO_CODEC: VIDEO_CODEC_COPY,
|
||||
CONF_AUDIO_CODEC: AUDIO_CODEC_COPY,
|
||||
},
|
||||
)
|
||||
bridge = HomeBridge("hass", run_driver, "Test Bridge")
|
||||
bridge.add_accessory(acc)
|
||||
|
||||
await acc.run_handler()
|
||||
|
||||
assert acc.aid == 2
|
||||
assert acc.category == 17 # Camera
|
||||
|
||||
acc.set_endpoints(MOCK_END_POINTS_TLV)
|
||||
session_info = acc.sessions[MOCK_START_STREAM_SESSION_UUID]
|
||||
|
||||
working_ffmpeg = _get_working_mock_ffmpeg()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.camera.DemoCamera.stream_source",
|
||||
return_value=None,
|
||||
), patch(
|
||||
"homeassistant.components.homekit.type_cameras.HAFFmpeg",
|
||||
return_value=working_ffmpeg,
|
||||
):
|
||||
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)
|
||||
await acc.stop_stream(session_info)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
expected_output = (
|
||||
"-map 0:v:0 -an -c:v copy -tune zerolatency -pix_fmt yuv420p -r 30 -b:v 299k "
|
||||
"-bufsize 1196k -maxrate 299k -payload_type 99 -ssrc {v_ssrc} -f rtp -srtp_out_suite "
|
||||
"AES_CM_128_HMAC_SHA1_80 -srtp_out_params zdPmNLWeI86DtLJHvVLI6YPvqhVeeiLsNtrAgbgL "
|
||||
"srtp://192.168.208.5:51246?rtcpport=51246&localrtcpport=51246&pkt_size=1316 -map 0:a:0 "
|
||||
"-vn -c:a copy -ac 1 -ar 24k -b:a 24k -bufsize 96k -payload_type 110 -ssrc {a_ssrc} "
|
||||
"-f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params "
|
||||
"shnETgfD+7xUQ8zRdsaytY11wu6CO73IJ+RZVJpU "
|
||||
"srtp://192.168.208.5:51108?rtcpport=51108&localrtcpport=51108&pkt_size=188"
|
||||
)
|
||||
|
||||
working_ffmpeg.open.assert_called_with(
|
||||
cmd=[],
|
||||
input_source="-i /dev/null",
|
||||
output=expected_output.format(**session_info),
|
||||
stdout_pipe=False,
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue