8000 Support multiple camera streams in HomeKit by bdraco · Pull Request #37968 · home-assistant/core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Support multiple camera streams in HomeKit #37968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions homeassistant/components/homekit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
CONF_VIDEO_CODEC = "video_codec"
CONF_VIDEO_MAP = "video_map"
CONF_VIDEO_PACKET_SIZE = "video_packet_size"
CONF_STREAM_COUNT = "stream_count"

# #### Config Defaults ####
DEFAULT_SUPPORT_AUDIO = False
Expand All @@ -72,6 +73,7 @@
DEFAULT_VIDEO_CODEC = VIDEO_CODEC_LIBX264
DEFAULT_VIDEO_MAP = "0:v:0"
DEFAULT_VIDEO_PACKET_SIZE = 1316
DEFAULT_STREAM_COUNT = 3

# #### Features ####
FEATURE_ON_OFF = "on_off"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/homekit/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "HomeKit",
"documentation": "https://www.home-assistant.io/integrations/homekit",
"requirements": [
"HAP-python==2.9.2",
"HAP-python==3.0.0",
"fnvhash==0.1.0",
"PyQRCode==1.2.1",
"base36==0.1.1",
Expand Down
53 changes: 22 additions & 31 deletions homeassistant/components/homekit/type_cameras.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from haffmpeg.core import HAFFmpeg
from pyhap.camera import (
STREAMING_STATUS,
VIDEO_CODEC_PARAM_LEVEL_TYPES,
VIDEO_CODEC_PARAM_PROFILE_ID_TYPES,
Camera as PyhapCamera,
Expand All @@ -24,7 +23,6 @@
from .accessories import TYPES, HomeAccessory
from .const import (
CHAR_MOTION_DETECTED,
CHAR_STREAMING_STRATUS,
CONF_AUDIO_CODEC,
CONF_AUDIO_MAP,
CONF_AUDIO_PACKET_SIZE,
Expand All @@ -33,6 +31,7 @@
CONF_MAX_HEIGHT,
CONF_MAX_WIDTH,
CONF_STREAM_ADDRESS,
CONF_STREAM_COUNT,
CONF_STREAM_SOURCE,
CONF_SUPPORT_AUDIO,
CONF_VIDEO_CODEC,
Expand All @@ -44,11 +43,11 @@
DEFAULT_MAX_FPS,
DEFAULT_MAX_HEIGHT,
DEFAULT_MAX_WIDTH,
DEFAULT_STREAM_COUNT,
DEFAULT_SUPPORT_AUDIO,
DEFAULT_VIDEO_CODEC,
DEFAULT_VIDEO_MAP,
DEFAULT_VIDEO_PACKET_SIZE,
SERV_CAMERA_RTP_STREAM_MANAGEMENT,
SERV_MOTION_SENSOR,
)
from .img_util import scale_jpeg_camera_image
Expand Down Expand Up @@ -121,6 +120,7 @@
CONF_VIDEO_CODEC: DEFAULT_VIDEO_CODEC,
CONF_AUDIO_PACKET_SIZE: DEFAULT_AUDIO_PACKET_SIZE,
CONF_VIDEO_PACKET_SIZE: DEFAULT_VIDEO_PACKET_SIZE,
CONF_STREAM_COUNT: DEFAULT_STREAM_COUNT,
}


Expand All @@ -131,7 +131,6 @@ class Camera(HomeAccessory, PyhapCamera):
def __init__(self, hass, driver, name, entity_id, aid, config):
"""Initialize a Camera accessory object."""
self._ffmpeg = hass.data[DATA_FFMPEG]
self._cur_session = None
for config_key in CONFIG_DEFAULTS:
if config_key not in config:
config[config_key] = CONFIG_DEFAULTS[config_key]
Expand Down Expand Up @@ -178,6 +177,7 @@ def __init__(self, hass, driver, name, entity_id, aid, config):
"audio": audio_options,
"address": stream_address,
"srtp": True,
"stream_count": config[CONF_STREAM_COUNT],
}

super().__init__(
Expand Down Expand Up @@ -313,51 +313,42 @@ async def start_stream(self, session_info, stream_config):
if not opened:
_LOGGER.error("Failed to open ffmpeg stream")
return False
session_info["stream"] = stream

_LOGGER.info(
"[%s] Started stream process - PID %d",
session_info["id"],
stream.process.pid,
)

ffmpeg_watcher = async_track_time_interval(
self.hass, self._async_ffmpeg_watch, FFMPEG_WATCH_INTERVAL
session_info["stream"] = stream
session_info[FFMPEG_PID] = stream.process.pid

async def watch_session(_):
await self._async_ffmpeg_watch(session_info["id"])

session_info[FFMPEG_WATCHER] = async_track_time_interval(
self.hass, watch_session, FFMPEG_WATCH_INTERVAL,
)
self._cur_session = {
FFMPEG_WATCHER: ffmpeg_watcher,
FFMPEG_PID: stream.process.pid,
SESSION_ID: session_info["id"],
}

return await self._async_ffmpeg_watch(0)
return await self._async_ffmpeg_watch(session_info["id"])

async def _async_ffmpeg_watch(self, _):
async def _async_ffmpeg_watch(self, session_id):
"""Check to make sure ffmpeg is still running and cleanup if not."""
ffmpeg_pid = self._cur_session[FFMPEG_PID]
session_id = self._cur_session[SESSION_ID]
ffmpeg_pid = self.sessions[session_id][FFMPEG_PID]
if pid_is_alive(ffmpeg_pid):
return True

_LOGGER.warning("Streaming process ended unexpectedly - PID %d", ffmpeg_pid)
self._async_stop_ffmpeg_watch()
self._async_set_streaming_available(session_id)
self._async_stop_ffmpeg_watch(session_id)
self.set_streaming_available(self.sessions[session_id]["stream_idx"])
return False

@callback
def _async_stop_ffmpeg_watch(self):
def _async_stop_ffmpeg_watch(self, session_id):
"""Cleanup a streaming session after stopping."""
if not self._cur_session:
if FFMPEG_WATCHER not in self.sessions[session_id]:
return
self._cur_session[FFMPEG_WATCHER]()
self._cur_session = None

@callback
def _async_set_streaming_available(self, session_id):
"""Free the session so they can start another."""
self.streaming_status = STREAMING_STATUS["AVAILABLE"]
self.get_service(SERV_CAMERA_RTP_STREAM_MANAGEMENT).get_characteristic(
CHAR_STREAMING_STRATUS
).notify()
self.sessions[session_id].pop(FFMPEG_WATCHER)()

async def stop_stream(self, session_info):
"""Stop the stream for the given ``session_id``."""
Expand All @@ -367,7 +358,7 @@ async def stop_stream(self, session_info):
_LOGGER.debug("No stream for session ID %s", session_id)
return

self._async_stop_ffmpeg_watch()
self._async_stop_ffmpeg_watch(session_id)

if not pid_is_alive(stream.process.pid):
_LOGGER.info("[%s] Stream already stopped", session_id)
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/homekit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
CONF_MAX_HEIGHT,
CONF_MAX_WIDTH,
CONF_STREAM_ADDRESS,
CONF_STREAM_COUNT,
CONF_STREAM_SOURCE,
CONF_SUPPORT_AUDIO,
CONF_VIDEO_CODEC,
Expand All @@ -53,6 +54,7 @@
DEFAULT_MAX_FPS,
DEFAULT_MAX_HEIGHT,
DEFAULT_MAX_WIDTH,
DEFAULT_STREAM_COUNT,
DEFAULT_SUPPORT_AUDIO,
DEFAULT_VIDEO_CODEC,
DEFAULT_VIDEO_MAP,
Expand Down Expand Up @@ -112,6 +114,9 @@
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_STREAM_COUNT, default=DEFAULT_STREAM_COUNT): vol.All(
vol.Coerce(int), vol.Range(min=1, max=10)
),
vol.Optional(CONF_VIDEO_CODEC, default=DEFAULT_VIDEO_CODEC): vol.In(
VALID_VIDEO_CODECS 9E7A
),
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Adafruit-SHT31==1.0.2
# Adafruit_BBIO==1.1.1

# homeassistant.components.homekit
HAP-python==2.9.2
HAP-python==3.0.0

# homeassistant.components.mastodon
Mastodon.py==1.5.1
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
-r requirements_test.txt

# homeassistant.components.homekit
HAP-python==2.9.2
HAP-python==3.0.0

# homeassistant.components.plugwise
Plugwise_Smile==1.1.0
Expand Down
0