diff --git a/docs/source/platypush/plugins/camera.gstreamer.rst b/docs/source/platypush/plugins/camera.gstreamer.rst new file mode 100644 index 00000000..5dc5ac37 --- /dev/null +++ b/docs/source/platypush/plugins/camera.gstreamer.rst @@ -0,0 +1,5 @@ +``camera.gstreamer`` +====================================== + +.. automodule:: platypush.plugins.camera.gstreamer + :members: diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index fcf2cfc5..d1713426 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -19,6 +19,7 @@ Plugins platypush/plugins/camera.android.ipcam.rst platypush/plugins/camera.cv.rst platypush/plugins/camera.ffmpeg.rst + platypush/plugins/camera.gstreamer.rst platypush/plugins/camera.ir.mlx90640.rst platypush/plugins/camera.pi.rst platypush/plugins/chat.irc.rst diff --git a/platypush/plugins/camera/gstreamer/__init__.py b/platypush/plugins/camera/gstreamer/__init__.py new file mode 100644 index 00000000..bb745b3c --- /dev/null +++ b/platypush/plugins/camera/gstreamer/__init__.py @@ -0,0 +1,94 @@ +from typing import Optional + +from PIL import Image +from PIL.Image import Image as ImageType + +from platypush.plugins.camera import CameraPlugin +from platypush.plugins.camera.gstreamer.model import GStreamerCamera +from platypush.common.gstreamer import Pipeline + + +class CameraGstreamerPlugin(CameraPlugin): + """ + Plugin to interact with a camera over GStreamer. + + Requires: + + * **gst-python** + * **pygobject** + + On Debian and derived systems: + + * ``[sudo] apt-get install python3-gi python3-gst-1.0`` + + On Arch and derived systems: + + * ``[sudo] pacman -S gst-python`` + + """ + + _camera_class = GStreamerCamera + + def __init__(self, device: Optional[str] = '/dev/video0', **opts): + """ + :param device: Path to the camera device (default ``/dev/video0``). + :param opts: Camera options - see constructor of :class:`platypush.plugins.camera.CameraPlugin`. + """ + super().__init__(device=device, **opts) + + def prepare_device(self, camera: GStreamerCamera) -> Pipeline: + pipeline = Pipeline() + src = pipeline.add_source('v4l2src', device=camera.info.device) + convert = pipeline.add('videoconvert') + assert camera.info and camera.info.resolution + + video_filter = pipeline.add( + 'capsfilter', + caps='video/x-raw,format=RGB,width={width},height={height},framerate={fps}/1'.format( + width=camera.info.resolution[0], + height=camera.info.resolution[1], + fps=camera.info.fps, + ), + ) + + sink = pipeline.add_sink('appsink', name='appsink', sync=False) + pipeline.link(src, convert, video_filter, sink) + return pipeline + + def start_camera( + self, camera: GStreamerCamera, preview: bool = False, *args, **kwargs + ): + super().start_camera(*args, camera=camera, preview=preview, **kwargs) + if camera.object: + camera.object.play() + + def release_device(self, camera: GStreamerCamera): + if camera.object: + camera.object.stop() + + def capture_frame(self, camera: GStreamerCamera, *_, **__) -> Optional[ImageType]: + if not (camera.info and camera.info.fps and camera.info.resolution): + return None + + timed_out = not camera.object.data_ready.wait( + timeout=5 + (1.0 / camera.info.fps) + ) + if timed_out: + self.logger.warning('Frame capture timeout') + return None + + data = camera.object.data + if data is None: + return None + + camera.object.data_ready.clear() + if ( + not data + and len(data) != camera.info.resolution[0] * camera.info.resolution[1] * 3 + ): + return None + + return Image.frombytes('RGB', camera.info.resolution, data) + + +# vim:sw=4:ts=4:et: diff --git a/platypush/plugins/camera/gstreamer/manifest.yaml b/platypush/plugins/camera/gstreamer/manifest.yaml new file mode 100644 index 00000000..43e2cae4 --- /dev/null +++ b/platypush/plugins/camera/gstreamer/manifest.yaml @@ -0,0 +1,16 @@ +manifest: + events: {} + install: + pip: + - numpy + - Pillow + - pygobject + apt: + - python3-gi + - python3-gst-1.0 + pacman: + - gst-python + - python-gobject + + package: platypush.plugins.camera.gstreamer + type: plugin diff --git a/platypush/plugins/camera/gstreamer/model.py b/platypush/plugins/camera/gstreamer/model.py new file mode 100644 index 00000000..48082c62 --- /dev/null +++ b/platypush/plugins/camera/gstreamer/model.py @@ -0,0 +1,10 @@ +from platypush.common.gstreamer import Pipeline +from platypush.plugins.camera import CameraInfo, Camera + + +class GStreamerCamera(Camera): + info: CameraInfo + object: Pipeline + + +# vim:sw=4:ts=4:et: