From 85efcad975d294955869c3eedb3514846c93e811 Mon Sep 17 00:00:00 2001 From: Krisjanis Veinbahs Date: Thu, 10 Mar 2022 12:34:28 +0200 Subject: [PATCH] Allow custom VLC command and custom audio device in streaming --- client/src/service/cli.py | 10 ++++++++++ client/src/service/click.py | 16 +++++++++++++++ client/src/service/managed_video_stream.py | 10 ++++++++-- client/src/static/vlc/stream.sh | 23 ++++++++++++++++++---- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/client/src/service/cli.py b/client/src/service/cli.py index 0a2d3b4..85d8a20 100755 --- a/client/src/service/cli.py +++ b/client/src/service/cli.py @@ -249,6 +249,8 @@ async def agent_hardware_camera( heartbeat_seconds: int, is_stream_existing: bool, stream_url_str: Optional[str], + video_vlc: Optional[str], + audio_device: Optional[str], video_device: Optional[str], video_width: Optional[int], video_height: Optional[int], @@ -810,6 +812,8 @@ async def quick_run( def parsed_video_config( is_stream_existing: bool, stream_url_str: Optional[str], + video_vlc: Optional[str], + audio_device: Optional[str], video_device: Optional[str], video_width: Optional[int], video_height: Optional[int], @@ -830,6 +834,8 @@ def parsed_video_config( if video_width is None: return Err(GenericClientError("Video stream width is required")) if video_height is None: return Err(GenericClientError("Video stream height is required")) return Ok(VLCStreamConfig.build( + video_vlc, + audio_device, video_device_result.value, video_width, video_height, @@ -846,6 +852,8 @@ async def agent_hardware_camera( heartbeat_seconds: int, is_stream_existing: bool, stream_url_str: Optional[str], + video_vlc: Optional[str], + audio_device: Optional[str], video_device: Optional[str], video_width: Optional[int], video_height: Optional[int], @@ -866,6 +874,8 @@ async def agent_hardware_camera( video_config_result = CLI.parsed_video_config( is_stream_existing, stream_url_str, + video_vlc, + audio_device, video_device, video_width, video_height, diff --git a/client/src/service/click.py b/client/src/service/click.py index eb26569..0b8713b 100755 --- a/client/src/service/click.py +++ b/client/src/service/click.py @@ -123,6 +123,16 @@ help='URL for an existing video source (if is_stream_existing), e.g. http://localhost:8081/webcam.ogg' ) # Video stream (non-existing) +STREAM_VLC_OPTION = click.option( + '--stream-vlc', '-r', "video_vlc", show_envvar=True, + type=str, envvar=f"{ENV_PREFIX}_STREAM_VLC", required=False, + help='VLC command to be used, e.g. "vlc" or "vlc-pi"' +) +STREAM_AUDIO_DEVICE_OPTION = click.option( + '--stream-audio', '-z', "audio_device", show_envvar=True, + type=str, envvar=f"{ENV_PREFIX}_STREAM_AUDIO_DEVICE", required=False, + help='VLC slave audio device to be used, e.g. "alsa://"' +) STREAM_DEVICE_PATH_OPTION = click.option( '--stream-device', '-d', "video_device", show_envvar=True, type=str, envvar=f"{ENV_PREFIX}_STREAM_DEVICE_PATH", required=False, @@ -618,6 +628,8 @@ async def exec(): @HEARTBEAT_SECONDS_OPTION @IS_STREAM_EXISTING_OPTION @STREAM_URL_OPTION +@STREAM_VLC_OPTION +@STREAM_AUDIO_DEVICE_OPTION @STREAM_DEVICE_PATH_OPTION @STREAM_WIDTH_OPTION @STREAM_HEIGHT_OPTION @@ -632,6 +644,8 @@ def agent_hardware_video( heartbeat_seconds: int, is_stream_existing: bool, stream_url_str: Optional[str], + video_vlc: Optional[str], + audio_device: Optional[str], video_device: Optional[str], video_width: Optional[int], video_height: Optional[int], @@ -650,6 +664,8 @@ async def exec(): heartbeat_seconds, is_stream_existing, stream_url_str, + video_vlc, + audio_device, video_device, video_width, video_height, diff --git a/client/src/service/managed_video_stream.py b/client/src/service/managed_video_stream.py index 8c7e4c2..0bfae59 100644 --- a/client/src/service/managed_video_stream.py +++ b/client/src/service/managed_video_stream.py @@ -6,8 +6,6 @@ import aiohttp from aiohttp import ClientSession, ClientResponse from result import Result, Err, Ok - -from src.domain.death import Death from src.domain.dip_client_error import DIPClientError, GenericClientError from src.domain.existing_file_path import ExistingFilePath from src.service.managed_url import ManagedURL, ManagedURLBuildError @@ -35,6 +33,8 @@ def to_url(self) -> Result[ManagedURL, ManagedURLBuildError]: @dataclass class VLCStreamConfig(VideoStreamConfig): + video_vlc: str + audio_device: str video_device: ExistingFilePath video_width: int video_height: int @@ -45,6 +45,8 @@ class VLCStreamConfig(VideoStreamConfig): @staticmethod def build( + video_vlc: Optional[str], + audio_device: Optional[str], video_device: ExistingFilePath, video_width: int, video_height: int, @@ -54,6 +56,8 @@ def build( port: Optional[int] ): return VLCStreamConfig( + video_vlc if video_vlc is not None else "vlc", + audio_device if audio_device is not None else "", video_device, video_width, video_height, @@ -69,6 +73,8 @@ def vlc_shell_args(self: 'VLCStreamConfig') -> List[str]: "bash", "-c", f"{vlc_script_path} " + f"-r \"{self.video_vlc}\" " + f"-z \"{self.audio_device}\" " f"-d \"{self.video_device.value}\" " f"-w \"{self.video_width}\" " f"-h \"{self.video_height}\" " diff --git a/client/src/static/vlc/stream.sh b/client/src/static/vlc/stream.sh index 54f1578..3a325e8 100755 --- a/client/src/static/vlc/stream.sh +++ b/client/src/static/vlc/stream.sh @@ -6,7 +6,9 @@ set -euo pipefail # Script usage help usage() { echo 'Streams a V4L2 video stream on a given TCP socket using VLC' - echo 'usage: -d /dev/video0 -w 1280 -h 720 -v 50 -s 44100 -a 50 -p 8081' + echo 'usage: -r "vlc" -z "alsa://" -d /dev/video0 -w 1280 -h 720 -v 50 -s 44100 -a 50 -p 8081' + echo ' -r VLC command' + echo ' -z V4L2 audio device' echo ' -d V4L2 device path' echo ' -w Video width in pixels' echo ' -h Video height in pixels' @@ -18,6 +20,8 @@ usage() { # Flag parsing sourced from: https://google.github.io/styleguide/shellguide.html # also from: https://stackoverflow.com/a/11279500/10806216 +vlc_command="vlc" +audio_device="alsa://" video_device="/dev/video0" video_width="1280" video_height="720" @@ -25,8 +29,10 @@ video_buffer_size="50" audio_sample_rate="44100" audio_buffer_size="50" port="8081" -while getopts 'd:w:h:v:s:a:p:' flag; do +while getopts 'r:z:d:w:h:v:s:a:p:' flag; do case "${flag}" in + r) vlc_command="${OPTARG}" ;; + z) audio_device="${OPTARG}" ;; d) video_device="${OPTARG}" ;; w) video_width="${OPTARG}" ;; h) video_height="${OPTARG}" ;; @@ -41,7 +47,17 @@ done # Print usage usage +# Audio device argument is optional +audio_device_argument="" +if [[ $audio_device ]] +then + audio_device_argument=":input-slave=${audio_device}" +fi + + # Env dump +echo "Audio device: ${audio_device}" +echo "Audio device argument: ${audio_device_argument}" echo "Video device: ${video_device}" echo "Video width: ${video_width}" echo "Video height: ${video_height}" @@ -52,8 +68,7 @@ echo "Port: ${port}" # Start video stream echo "Starting video stream..." -vlc v4l2:// \ - :input-slave=alsa:// \ +"${vlc_command}" v4l2:// ${audio_device_argument} \ :v4l2-standard=1 \ :v4l2-dev=${video_device} \ :v4l2-width=${video_width} \