Log ffmpeg errors for homekit cameras (#46545)

This commit is contained in:
J. Nick Koston 2021-02-15 01:39:51 -10:00 committed by GitHub
parent bed29fd4b1
commit c5b9ad83c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 2 deletions

View File

@ -1,8 +1,9 @@
"""Class to hold all camera accessories."""
import asyncio
from datetime import timedelta
import logging
from haffmpeg.core import HAFFmpeg
from haffmpeg.core import FFMPEG_STDERR, HAFFmpeg
from pyhap.camera import (
VIDEO_CODEC_PARAM_LEVEL_TYPES,
VIDEO_CODEC_PARAM_PROFILE_ID_TYPES,
@ -114,6 +115,7 @@ RESOLUTIONS = [
VIDEO_PROFILE_NAMES = ["baseline", "main", "high"]
FFMPEG_WATCH_INTERVAL = timedelta(seconds=5)
FFMPEG_LOGGER = "ffmpeg_logger"
FFMPEG_WATCHER = "ffmpeg_watcher"
FFMPEG_PID = "ffmpeg_pid"
SESSION_ID = "session_id"
@ -371,7 +373,12 @@ class Camera(HomeAccessory, PyhapCamera):
_LOGGER.debug("FFmpeg output settings: %s", output)
stream = HAFFmpeg(self._ffmpeg.binary)
opened = await stream.open(
cmd=[], input_source=input_source, output=output, stdout_pipe=False
cmd=[],
input_source=input_source,
output=output,
extra_cmd="-hide_banner -nostats",
stderr_pipe=True,
stdout_pipe=False,
)
if not opened:
_LOGGER.error("Failed to open ffmpeg stream")
@ -386,9 +393,14 @@ class Camera(HomeAccessory, PyhapCamera):
session_info["stream"] = stream
session_info[FFMPEG_PID] = stream.process.pid
stderr_reader = await stream.get_reader(source=FFMPEG_STDERR)
async def watch_session(_):
await self._async_ffmpeg_watch(session_info["id"])
session_info[FFMPEG_LOGGER] = asyncio.create_task(
self._async_log_stderr_stream(stderr_reader)
)
session_info[FFMPEG_WATCHER] = async_track_time_interval(
self.hass,
watch_session,
@ -397,6 +409,16 @@ class Camera(HomeAccessory, PyhapCamera):
return await self._async_ffmpeg_watch(session_info["id"])
async def _async_log_stderr_stream(self, stderr_reader):
"""Log output from ffmpeg."""
_LOGGER.debug("%s: ffmpeg: started", self.display_name)
while True:
line = await stderr_reader.readline()
if line == b"":
return
_LOGGER.debug("%s: ffmpeg: %s", self.display_name, line.rstrip())
async def _async_ffmpeg_watch(self, session_id):
"""Check to make sure ffmpeg is still running and cleanup if not."""
ffmpeg_pid = self.sessions[session_id][FFMPEG_PID]
@ -414,6 +436,7 @@ class Camera(HomeAccessory, PyhapCamera):
if FFMPEG_WATCHER not in self.sessions[session_id]:
return
self.sessions[session_id].pop(FFMPEG_WATCHER)()
self.sessions[session_id].pop(FFMPEG_LOGGER).cancel()
async def stop_stream(self, session_info):
"""Stop the stream for the given ``session_id``."""

View File

@ -99,6 +99,7 @@ def _get_exits_after_startup_mock_ffmpeg():
ffmpeg.open = AsyncMock(return_value=True)
ffmpeg.close = AsyncMock(return_value=True)
ffmpeg.kill = AsyncMock(return_value=True)
ffmpeg.get_reader = AsyncMock()
return ffmpeg
@ -108,6 +109,7 @@ def _get_working_mock_ffmpeg():
ffmpeg.open = AsyncMock(return_value=True)
ffmpeg.close = AsyncMock(return_value=True)
ffmpeg.kill = AsyncMock(return_value=True)
ffmpeg.get_reader = AsyncMock()
return ffmpeg
@ -118,6 +120,7 @@ def _get_failing_mock_ffmpeg():
ffmpeg.open = AsyncMock(return_value=False)
ffmpeg.close = AsyncMock(side_effect=OSError)
ffmpeg.kill = AsyncMock(side_effect=OSError)
ffmpeg.get_reader = AsyncMock()
return ffmpeg
@ -189,6 +192,8 @@ async def test_camera_stream_source_configured(hass, run_driver, events):
input_source="-i /dev/null",
output=expected_output.format(**session_info),
stdout_pipe=False,
extra_cmd="-hide_banner -nostats",
stderr_pipe=True,
)
await _async_setup_endpoints(hass, acc)
@ -472,6 +477,8 @@ async def test_camera_stream_source_configured_and_copy_codec(hass, run_driver,
input_source="-i /dev/null",
output=expected_output.format(**session_info),
stdout_pipe=False,
extra_cmd="-hide_banner -nostats",
stderr_pipe=True,
)
@ -542,6 +549,8 @@ async def test_camera_streaming_fails_after_starting_ffmpeg(hass, run_driver, ev
input_source="-i /dev/null",
output=expected_output.format(**session_info),
stdout_pipe=False,
extra_cmd="-hide_banner -nostats",
stderr_pipe=True,
)