Added `stream_on_start` argument to `camera` plugins.
continuous-integration/drone/push Build is passing Details

It replaces the functionalities of the deprecated `camera` backends.
This commit is contained in:
Fabio Manganiello 2024-02-24 01:28:25 +01:00
parent 9cf95125a6
commit 9ad9bd20e4
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
1 changed files with 84 additions and 30 deletions

View File

@ -86,6 +86,7 @@ class CameraPlugin(RunnablePlugin, ABC):
stream_format: str = 'mjpeg', stream_format: str = 'mjpeg',
listen_port: Optional[int] = 5000, listen_port: Optional[int] = 5000,
bind_address: str = '0.0.0.0', bind_address: str = '0.0.0.0',
stream_on_start: bool = False,
ffmpeg_bin: str = 'ffmpeg', ffmpeg_bin: str = 'ffmpeg',
input_codec: Optional[str] = None, input_codec: Optional[str] = None,
output_codec: Optional[str] = None, output_codec: Optional[str] = None,
@ -94,41 +95,57 @@ class CameraPlugin(RunnablePlugin, ABC):
""" """
:param device: Identifier of the default capturing device. :param device: Identifier of the default capturing device.
:param resolution: Default resolution, as a tuple of two integers. :param resolution: Default resolution, as a tuple of two integers.
:param frames_dir: Directory where the camera frames will be stored (default: :param frames_dir: Directory where the camera frames will be stored
``~/.local/share/platypush/<plugin.name>/frames``) (default: ``~/.local/share/platypush/<plugin.name>/frames``)
:param warmup_frames: Cameras usually take a while to adapt their :param warmup_frames: Cameras usually take a while to adapt their
luminosity and focus to the environment when taking a picture. luminosity and focus to the environment when taking a picture.
This parameter allows you to specify the number of "warmup" frames This parameter allows you to specify the number of "warmup" frames
to capture upon picture command before actually capturing a frame to capture upon picture command before actually capturing a frame
(default: 5 but you may want to calibrate this parameter for your (default: 5 but you may want to calibrate this parameter for your
camera) camera)
:param warmup_seconds: Number of seconds to wait before a picture is taken or the first frame of a :param warmup_seconds: Number of seconds to wait before a picture is
video/sequence is captured (default: 0). taken or the first frame of a video/sequence is captured (default:
:param capture_timeout: Maximum number of seconds to wait between the programmed termination of a capture 0).
session and the moment the device is released. :param capture_timeout: Maximum number of seconds to wait between the
:param scale_x: If set, the images will be scaled along the x-axis by the specified factor programmed termination of a capture session and the moment the
:param scale_y: If set, the images will be scaled along the y-axis by the specified factor device is released.
:param scale_x: If set, the images will be scaled along the x-axis by
the specified factor
:param scale_y: If set, the images will be scaled along the y-axis by
the specified factor
:param color_transform: Color transformation to apply to the images. :param color_transform: Color transformation to apply to the images.
:param grayscale: Whether the output should be converted to grayscale. :param grayscale: Whether the output should be converted to grayscale.
:param rotate: If set, the images will be rotated by the specified number of degrees :param rotate: If set, the images will be rotated by the specified
number of degrees
:param fps: Frames per second (default: 25). :param fps: Frames per second (default: 25).
:param horizontal_flip: If set, the images will be flipped on the horizontal axis. :param horizontal_flip: If set, the images will be flipped on the
:param vertical_flip: If set, the images will be flipped on the vertical axis. horizontal axis.
:param listen_port: Default port to be used for streaming over TCP (default: 5000). :param vertical_flip: If set, the images will be flipped on the vertical
:param bind_address: Default bind address for TCP streaming (default: 0.0.0.0, accept any connections). axis.
:param input_codec: Specify the ffmpeg video codec (``-vcodec``) used for the input. :param listen_port: Default port to be used for streaming over TCP
:param output_codec: Specify the ffmpeg video codec (``-vcodec``) to be used for encoding the output. For some (default: 5000).
ffmpeg output formats (e.g. ``h264`` and ``rtp``) this may default to ``libxvid``. :param bind_address: Default bind address for TCP streaming (default:
0.0.0.0, accept connections on any network interface).
:param stream_on_start: If set, the camera will start streaming on the
specified ``bind_address`` and ``listen_port`` as soon as the plugin
is started. Otherwise, the stream will be started only when the
:meth:`.start_streaming` method is called. Default: False.
:param input_codec: Specify the ffmpeg video codec (``-vcodec``) used
for the input.
:param output_codec: Specify the ffmpeg video codec (``-vcodec``) to be
used for encoding the output. For some ffmpeg output formats (e.g.
``h264`` and ``rtp``) this may default to ``libxvid``.
:param input_format: Plugin-specific format/type for the input stream. :param input_format: Plugin-specific format/type for the input stream.
:param output_format: Plugin-specific format/type for the output videos. :param output_format: Plugin-specific format/type for the output videos.
:param ffmpeg_bin: Path to the ffmpeg binary (default: ``ffmpeg``). :param ffmpeg_bin: Path to the ffmpeg binary (default: ``ffmpeg``).
:param stream_format: Default format for the output when streamed to a network device. Available: :param stream_format: Default format for the output when streamed to a
network device. Available:
- ``MJPEG`` (default) - ``MJPEG`` (default)
- ``H264`` (over ``ffmpeg``) - ``H264`` (over ``ffmpeg``)
- ``H265`` (over ``ffmpeg``) - ``H265`` (over ``ffmpeg``)
- ``MKV`` (over ``ffmpeg``) - ``MKV`` (over ``ffmpeg``)
- ``MP4`` (over ``ffmpeg``) - ``MP4`` (over ``ffmpeg``)
""" """
super().__init__(**kwargs) super().__init__(**kwargs)
@ -137,6 +154,7 @@ class CameraPlugin(RunnablePlugin, ABC):
plugin_name = get_plugin_name_by_class(self) plugin_name = get_plugin_name_by_class(self)
assert isinstance(workdir, str) and plugin_name assert isinstance(workdir, str) and plugin_name
self.workdir = os.path.join(workdir, plugin_name) self.workdir = os.path.join(workdir, plugin_name)
self._stream_on_start = stream_on_start
pathlib.Path(self.workdir).mkdir(mode=0o755, exist_ok=True, parents=True) pathlib.Path(self.workdir).mkdir(mode=0o755, exist_ok=True, parents=True)
self.camera_info = self._camera_info_class( self.camera_info = self._camera_info_class(
@ -322,7 +340,9 @@ class CameraPlugin(RunnablePlugin, ABC):
raise NotImplementedError() raise NotImplementedError()
@staticmethod @staticmethod
def store_frame(frame, filepath: str, format: Optional[str] = None): def store_frame( # pylint: disable=redefined-builtin
frame, filepath: str, format: Optional[str] = None
):
""" """
Capture a frame to the filesystem using the ``PIL`` library - it can be overridden by derived classes. Capture a frame to the filesystem using the ``PIL`` library - it can be overridden by derived classes.
@ -346,9 +366,9 @@ class CameraPlugin(RunnablePlugin, ABC):
def _store_frame( def _store_frame(
self, self,
frame, frame,
*args,
frames_dir: Optional[str] = None, frames_dir: Optional[str] = None,
image_file: Optional[str] = None, image_file: Optional[str] = None,
*args,
**kwargs, **kwargs,
) -> str: ) -> str:
""" """
@ -792,21 +812,39 @@ class CameraPlugin(RunnablePlugin, ABC):
self, self,
device: Optional[Union[int, str]] = None, device: Optional[Union[int, str]] = None,
duration: Optional[float] = None, duration: Optional[float] = None,
stream_format: str = 'mkv', stream_format: Optional[str] = None,
**camera, **camera,
) -> dict: ) -> dict:
""" """
Expose the video stream of a camera over a TCP connection. Expose the video stream of a camera over a TCP connection.
:param device: Name/path/ID of the device to capture from (default: None, use the default device). When the streaming is started, the plugin will listen on the specified
:param duration: Streaming thread duration (default: until :meth:`.stop_streaming` is called). ``bind_address`` and ``listen_port`` and stream camera frames to
:param stream_format: Format of the output stream - e.g. ``h264``, ``mjpeg``, ``mkv`` etc. (default: ``mkv``). connected clients. If ``stream_format`` is a video format (H264, H265,
MKV, MP4 etc.) then the camera stream can be viewed using a video
player - for example, using ``vlc``:
.. code-block:: bash
vlc tcp://<host>:<port>
:param device: Name/path/ID of the device to capture from (default:
None, use the default device).
:param duration: Streaming thread duration (default: until
:meth:`.stop_streaming` is called).
:param stream_format: Format of the output stream - e.g. ``h264``,
``mjpeg``, ``mkv`` etc. If not specified, the ``stream_format``
configured on the plugin will be used.
:param camera: Camera object properties - see constructor parameters. :param camera: Camera object properties - see constructor parameters.
:return: The status of the device. :return: The status of the device.
""" """
camera = self.open_device( camera = self.open_device(
device=device, stream=True, stream_format=stream_format, **camera device=device,
stream=True,
stream_format=stream_format or self.camera_info.stream_format,
**camera,
) )
return self._start_streaming(camera, duration, stream_format) # type: ignore return self._start_streaming(camera, duration, stream_format) # type: ignore
def _start_streaming( def _start_streaming(
@ -981,7 +1019,23 @@ class CameraPlugin(RunnablePlugin, ABC):
return 0 return 0
def main(self): def main(self):
self.wait_stop() if not self._stream_on_start:
self.wait_stop()
return
while not self.should_stop():
if self._stream_on_start:
self.start_streaming()
cameras = list(self._streams.values())
if not cameras:
self.logger.warning('No camera devices could be streamed')
self.wait_stop()
break
camera = cameras[0]
wait_for_either(self._should_stop, camera.stop_stream_event)
self.stop_streaming()
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et: