A versatile and extensible platform for home and life automation with hundreds of supported integrations https://platypush.tech
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

76 lines
3.0 KiB

import signal
import subprocess
from typing import Optional, Tuple
from PIL import Image
from PIL.Image import Image as ImageType
from platypush.plugins.camera import CameraPlugin
from platypush.plugins.camera.ffmpeg.model import FFmpegCamera, FFmpegCameraInfo
class CameraFfmpegPlugin(CameraPlugin):
Plugin to interact with a camera over FFmpeg.
* **ffmpeg** package installed on the system.
_camera_class = FFmpegCamera
_camera_info_class = FFmpegCameraInfo
def __init__(self, device: Optional[str] = '/dev/video0', input_format: str = 'v4l2', ffmpeg_args: Tuple[str] = (),
:param device: Path to the camera device (default: ``/dev/video0``).
:param input_format: FFmpeg input format for the the camera device (default: ``v4l2``).
:param ffmpeg_args: Extra options to be passed to the FFmpeg executable.
:param opts: Camera options - see constructor of :class:`platypush.plugins.camera.CameraPlugin`.
super().__init__(device=device, input_format=input_format, **opts)
self.camera_info.ffmpeg_args = ffmpeg_args or ()
def prepare_device(self, camera: FFmpegCamera) -> subprocess.Popen:
warmup_seconds = self._get_warmup_seconds(camera)
ffmpeg = [camera.info.ffmpeg_bin, '-y', '-f', camera.info.input_format, '-i', camera.info.device, '-s',
'{}x{}'.format(*camera.info.resolution), '-ss', str(warmup_seconds),
*(('-r', str(camera.info.fps)) if camera.info.fps else ()),
'-pix_fmt', 'rgb24', '-f', 'rawvideo', *camera.info.ffmpeg_args, '-']
self.logger.info('Running FFmpeg: {}'.format(' '.join(ffmpeg)))
proc = subprocess.Popen(ffmpeg, stdout=subprocess.PIPE)
# Start in suspended mode
return proc
def start_camera(self, camera: FFmpegCamera, preview: bool = False, *args, **kwargs):
super().start_camera(*args, camera=camera, preview=preview, **kwargs)
if camera.object:
def release_device(self, camera: FFmpegCamera):
if camera.object:
if camera.object.stdout:
camera.object = None
def wait_capture(self, camera: FFmpegCamera) -> None:
if camera.object and camera.object.poll() is None:
except Exception as e:
self.logger.warning('Error on FFmpeg capture wait: {}'.format(str(e)))
def capture_frame(self, camera: FFmpegCamera, *args, **kwargs) -> Optional[ImageType]:
raw_size = camera.info.resolution[0] * camera.info.resolution[1] * 3
data = camera.object.stdout.read(raw_size)
if len(data) < raw_size:
return Image.frombytes('RGB', camera.info.resolution, data)
# vim:sw=4:ts=4:et: