diff --git a/flake.nix b/flake.nix index 27e3f80..f3745d7 100644 --- a/flake.nix +++ b/flake.nix @@ -306,6 +306,7 @@ fi ''; + v4l2-play = pkgs.callPackage ./pkgs/v4l2-play {}; device-mon = pkgs.callPackage ./pkgs/device-mon {}; #pdoc-docs = (pkgs.callPackage ./pkgs/pdocs.nix {}).pdocs; #pdoc3-docs = (pkgs.callPackage ./pkgs/pdocs.nix {}).pdocs3; diff --git a/pkgs/v4l2-play/default.nix b/pkgs/v4l2-play/default.nix new file mode 100644 index 0000000..e3cd004 --- /dev/null +++ b/pkgs/v4l2-play/default.nix @@ -0,0 +1,60 @@ +{ + lib, + pkgs, +}: + +let + runtimeInputs = with pkgs; [ + ffmpeg + v4l-utils + alsa-utils + gnome.zenity + lxterminal + ]; +in + +pkgs.stdenvNoCC.mkDerivation (finalAttrs: { + name = "v4l2-play"; + meta.mainProgram = "v4l2-play"; + + nativeBuildInputs = with pkgs; [ + makeWrapper + copyDesktopItems + ]; + + dontUnpack = true; + dontBuild = true; + dontConfigure = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin + cp ${./script.sh} $out/bin/v4l2-play + chmod +wx $out/bin/v4l2-play + + substituteInPlace $out/bin/v4l2-play \ + --replace-fail \ + "#!/usr/bin/env nix-shell" \ + "#!${pkgs.runtimeShell}" + + wrapProgram $out/bin/v4l2-play \ + --prefix PATH : ${lib.makeBinPath runtimeInputs} + + runHook postInstall + ''; + + desktopItems = pkgs.makeDesktopItem { + name = "v4l2-play"; + desktopName = "View video device"; + exec = "v4l2-play"; + keywords = [ + "elgato" + "passthrough" + "v4l2" + "video4linux2" + "capture card" + ]; + }; + +}) diff --git a/pkgs/v4l2-play/script.sh b/pkgs/v4l2-play/script.sh new file mode 100644 index 0000000..bfe5e63 --- /dev/null +++ b/pkgs/v4l2-play/script.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p ffmpeg v4l-utils alsa-utils gnome.zenity lxterminal +set -euo pipefail + +#if [[ -z ${_IS_IN_GUI:-} ]]; then +# exec lxterminal --command "_IS_IN_GUI=1 $(printf "%q" "$(realpath "$0")")" & +#fi + +video_devices=() +audio_devices=() + +tab=$(printf '\t') +current_name= +while IFS= read line; do + if ! [[ $line == "$tab"* ]]; then + current_name="$line" + else + video_devices+=("$(echo "$line - $current_name" | xargs)") + fi +done < <( v4l2-ctl --list-devices ) + +printf "%s\n" "${video_devices[@]}" + +current_name= +current_device= +current_subdevice= +while IFS= read line; do + if ! [[ $line == " "* ]]; then + current_name=$(echo $line | cut -d: -f2- | cut -c2-) + current_device=$(echo $line | grep -E 'card [0-9]+:' -o | tr -dc '[:digit:]' || true) + elif [[ $line == " Subdevice #"* ]]; then + current_subdevice=$(echo $line | grep -E 'Subdevice #[0-9]+:' -o | tr -dc '[:digit:]' || true) + #audio_devices+=("$(echo "$line - $current_name" | xargs)") + audio_devices+=("hw:$current_device,$current_subdevice - $current_name") + else + : + fi +done < <( arecord -l ) + + +printf "%s\n" "${audio_devices[@]}" + + +zenity_cells=() +maybe_true=TRUE +for video_device in "${video_devices[@]}"; do + if grep <<<"$video_device" -q Elgato; then + zenity_cells+=("$maybe_true") + maybe_true=FALSE + else + zenity_cells+=(FALSE) + fi + zenity_cells+=("$video_device") +done +selected_video_device=$( + zenity --list --text="Which video device" --radiolist --column=Select --column=Device "${zenity_cells[@]}" \ + | cut -d' ' -f1 +) + +zenity_cells=() +maybe_true=TRUE +for audio_device in "${audio_devices[@]}"; do + if grep <<<"$audio_device" -q Elgato; then + zenity_cells+=("$maybe_true") + maybe_true=FALSE + else + zenity_cells+=(FALSE) + fi + zenity_cells+=("$audio_device") +done +selected_audio_device=$( + zenity --list --text="Which audio device" --radiolist --column=Select --column=Device "${zenity_cells[@]}" \ + | cut -d' ' -f1 +) + +ffmpeg_common=( + -fflags nobuffer + -flags low_delay +) +ffmpeg_args=( + -f v4l2 + -input_format nv12 + -video_size 1920x1080 + -framerate 60 + #-i /dev/video3 + -i "$selected_video_device" + -f alsa + #-i hw:5,0 + -i "$selected_audio_device" +) + +set -x +ffmpeg "${ffmpeg_common[@]}" "${ffmpeg_args[@]}" -c copy -f nut pipe: | ffplay "${ffmpeg_common[@]}" -f nut -i pipe: