1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
"""LiveStream class, representing a Logi Circle camera's live stream"""
# coding: utf-8
# vim:sw=4:ts=4:et:
import logging
import subprocess
from .const import (ACCESSORIES_ENDPOINT,
LIVE_IMAGE_ENDPOINT,
LIVE_RTSP_ENDPOINT,
ACCEPT_IMAGE_HEADER,
DEFAULT_IMAGE_QUALITY,
DEFAULT_IMAGE_REFRESH)
from .utils import _stream_to_file
_LOGGER = logging.getLogger(__name__)
class LiveStream():
"""Generic implementation for Logi Circle live stream."""
def __init__(self, logi, camera):
"""Initialise Logi Camera object."""
self.logi = logi
self.camera_id = camera.id
def get_jpeg_url(self):
"""Get URL for camera JPEG snapshot"""
url = '%s/%s%s' % (ACCESSORIES_ENDPOINT, self.camera_id, LIVE_IMAGE_ENDPOINT)
return url
async def download_jpeg(self,
quality=DEFAULT_IMAGE_QUALITY,
refresh=DEFAULT_IMAGE_REFRESH,
filename=None):
"""Download the most recent snapshot image for this camera"""
url = self.get_jpeg_url()
params = {'quality': quality, 'refresh': str(refresh).lower()}
image = await self.logi._fetch(url=url, raw=True, headers=ACCEPT_IMAGE_HEADER, params=params)
if filename:
await _stream_to_file(image.content, filename)
image.close()
return True
content = await image.read()
image.close()
return content
async def get_rtsp_url(self):
"""Get RTSP stream URL."""
# Request RTSP stream
url = '%s/%s%s' % (ACCESSORIES_ENDPOINT, self.camera_id, LIVE_RTSP_ENDPOINT)
stream_resp_payload = await self.logi._fetch(url=url)
# Return time-limited RTSP URI
rtsp_uri = stream_resp_payload["rtsp_uri"].replace('rtsp://', 'rtsps://')
return rtsp_uri
async def download_rtsp(self,
duration, # in seconds
filename,
ffmpeg_bin=None,
blocking=False):
"""Downloads the live stream into a specific file for a specific duration"""
ffmpeg_bin = ffmpeg_bin or self.logi.ffmpeg_path
# Bail now if ffmpeg is missing
if ffmpeg_bin is None:
raise RuntimeError(
"This method requires ffmpeg to be installed and available from the current execution context.")
rtsp_uri = await self.get_rtsp_url()
subprocess_method = getattr(subprocess, 'check_call' if blocking else 'Popen')
subprocess_method(
[ffmpeg_bin, "-i", rtsp_uri, "-t", str(duration),
"-vcodec", "copy", "-acodec", "copy", filename],
stderr=subprocess.DEVNULL
)
|