Support multiple camera streams in HomeKit (#37968)

* Support multiple camera stream in HomeKit

* Update homeassistant/components/homekit/type_cameras.py

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Revert "Update homeassistant/components/homekit/type_cameras.py"

This reverts commit d7624c5bff.

* Update homeassistant/components/homekit/type_cameras.py

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/homekit/type_cameras.py

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* black

* bump pyhap

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/38212/head
J. Nick Koston 2020-07-25 07:12:14 -10:00 committed by GitHub
parent bbc8748e3b
commit 3206f4dc83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 32 additions and 34 deletions

View File

@ -55,6 +55,7 @@ CONF_SUPPORT_AUDIO = "support_audio"
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
@ -72,6 +73,7 @@ DEFAULT_SAFE_MODE = False
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"

View File

@ -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",

View File

@ -5,7 +5,6 @@ import logging
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,
@ -24,7 +23,6 @@ from homeassistant.util import get_local_ip
from .accessories import TYPES, HomeAccessory
from .const import (
CHAR_MOTION_DETECTED,
CHAR_STREAMING_STRATUS,
CONF_AUDIO_CODEC,
CONF_AUDIO_MAP,
CONF_AUDIO_PACKET_SIZE,
@ -33,6 +31,7 @@ from .const import (
CONF_MAX_HEIGHT,
CONF_MAX_WIDTH,
CONF_STREAM_ADDRESS,
CONF_STREAM_COUNT,
CONF_STREAM_SOURCE,
CONF_SUPPORT_AUDIO,
CONF_VIDEO_CODEC,
@ -44,11 +43,11 @@ from .const import (
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
@ -121,6 +120,7 @@ CONFIG_DEFAULTS = {
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,
}
@ -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]
@ -178,6 +177,7 @@ class Camera(HomeAccessory, PyhapCamera):
"audio": audio_options,
"address": stream_address,
"srtp": True,
"stream_count": config[CONF_STREAM_COUNT],
}
super().__init__(
@ -313,51 +313,42 @@ class Camera(HomeAccessory, PyhapCamera):
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``."""
@ -367,7 +358,7 @@ class Camera(HomeAccessory, PyhapCamera):
_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)

View File

@ -41,6 +41,7 @@ from .const import (
CONF_MAX_HEIGHT,
CONF_MAX_WIDTH,
CONF_STREAM_ADDRESS,
CONF_STREAM_COUNT,
CONF_STREAM_SOURCE,
CONF_SUPPORT_AUDIO,
CONF_VIDEO_CODEC,
@ -53,6 +54,7 @@ from .const import (
DEFAULT_MAX_FPS,
DEFAULT_MAX_HEIGHT,
DEFAULT_MAX_WIDTH,
DEFAULT_STREAM_COUNT,
DEFAULT_SUPPORT_AUDIO,
DEFAULT_VIDEO_CODEC,
DEFAULT_VIDEO_MAP,
@ -112,6 +114,9 @@ CAMERA_SCHEMA = BASIC_INFO_SCHEMA.extend(
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
),

View File

@ -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

View File

@ -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