diff options
| -rw-r--r-- | modules/video-audio-recording.el | 44 | ||||
| -rw-r--r-- | tests/test-video-audio-recording--build-audio-command.el | 76 | ||||
| -rw-r--r-- | tests/test-video-audio-recording--build-video-command.el | 16 |
3 files changed, 117 insertions, 19 deletions
diff --git a/modules/video-audio-recording.el b/modules/video-audio-recording.el index 94f90099..6c00a2df 100644 --- a/modules/video-audio-recording.el +++ b/modules/video-audio-recording.el @@ -843,11 +843,32 @@ On X11: ffmpeg captures screen directly via x11grab with PulseAudio audio." "-filter_complex \"[1:a]volume=%.1f[mic];[2:a]volume=%.1f[sys];[mic][sys]amerge=inputs=2[out]\" " "-map 0:v -map \"[out]\" " "%s") - mic-device - system-device + (shell-quote-argument mic-device) + (shell-quote-argument system-device) cj/recording-mic-boost cj/recording-system-volume - filename))) + (shell-quote-argument filename)))) + +(defun cj/recording--build-audio-command (mic-device system-device filename) + "Build the ffmpeg shell command string for audio-only recording. +MIC-DEVICE and SYSTEM-DEVICE are PulseAudio device names. FILENAME is +the output .m4a path. Mixes mic + system monitor into a single AAC file." + (format (concat "ffmpeg " + "-f pulse -i %s " ; Input 0: microphone + "-f pulse -i %s " ; Input 1: system audio monitor + "-filter_complex \"" + "[0:a]volume=%.1f[mic];" + "[1:a]volume=%.1f[sys];" + "[mic][sys]amix=inputs=2:duration=longest[out]\" " + "-map \"[out]\" " + "-c:a aac " + "-b:a 64k " + "%s") + (shell-quote-argument mic-device) + (shell-quote-argument system-device) + cj/recording-mic-boost + cj/recording-system-volume + (shell-quote-argument filename))) (defun cj/ffmpeg-record-video (directory) "Start a video recording, saving output to DIRECTORY. @@ -898,22 +919,7 @@ The filter graph mixes two PulseAudio inputs: (name (format-time-string "%Y-%m-%d-%H-%M-%S")) (filename (expand-file-name (concat name ".m4a") location)) (ffmpeg-command - (format (concat "ffmpeg " - "-f pulse -i %s " ; Input 0: microphone - "-f pulse -i %s " ; Input 1: system audio monitor - "-filter_complex \"" - "[0:a]volume=%.1f[mic];" - "[1:a]volume=%.1f[sys];" - "[mic][sys]amix=inputs=2:duration=longest[out]\" " - "-map \"[out]\" " - "-c:a aac " - "-b:a 64k " - "%s") - mic-device - system-device - cj/recording-mic-boost - cj/recording-system-volume - filename))) + (cj/recording--build-audio-command mic-device system-device filename))) (message "Recording from mic: %s + ALL system outputs" mic-device) (cj/log-silently "Audio recording ffmpeg command: %s" ffmpeg-command) (setq cj/audio-recording-ffmpeg-process diff --git a/tests/test-video-audio-recording--build-audio-command.el b/tests/test-video-audio-recording--build-audio-command.el new file mode 100644 index 00000000..54e5f56c --- /dev/null +++ b/tests/test-video-audio-recording--build-audio-command.el @@ -0,0 +1,76 @@ +;;; test-video-audio-recording--build-audio-command.el --- Tests for audio command builder -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/recording--build-audio-command. +;; Verifies correct ffmpeg command construction for audio-only recording +;; (mic + system monitor mixed to M4A/AAC), including shell quoting of +;; device names and output paths. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +(require 'video-audio-recording) + +;;; Normal Cases + +(ert-deftest test-video-audio-recording--build-audio-command-normal-uses-ffmpeg-pulse () + "Normal: audio command uses ffmpeg with two PulseAudio inputs mixed to AAC." + (let ((cj/recording-mic-boost 2.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-audio-command "mic" "sys" "/tmp/out.m4a"))) + (should (string-match-p "ffmpeg" cmd)) + (should (string-match-p "-f pulse -i" cmd)) + (should (string-match-p "amix=inputs=2" cmd)) + (should (string-match-p "-c:a aac" cmd))))) + +(ert-deftest test-video-audio-recording--build-audio-command-normal-devices-in-command () + "Normal: both mic and system device names appear in the command." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-audio-command + "alsa_input.usb-Jabra-00.mono" + "alsa_output.usb-JDS-00.monitor" + "/tmp/out.m4a"))) + (should (string-match-p "alsa_input.usb-Jabra-00.mono" cmd)) + (should (string-match-p "alsa_output.usb-JDS-00.monitor" cmd))))) + +(ert-deftest test-video-audio-recording--build-audio-command-normal-volume-in-filter () + "Normal: volume settings appear in the filter_complex expression." + (let ((cj/recording-mic-boost 1.5) + (cj/recording-system-volume 0.7)) + (let ((cmd (cj/recording--build-audio-command "mic" "sys" "/tmp/out.m4a"))) + (should (string-match-p "volume=1\\.5" cmd)) + (should (string-match-p "volume=0\\.7" cmd))))) + +;;; Boundary Cases + +(ert-deftest test-video-audio-recording--build-audio-command-boundary-device-quoted () + "Boundary: device names with spaces are shell-quoted." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-audio-command + "device with spaces" "sys" "/tmp/out.m4a"))) + (should (string-match-p "device\\\\ with\\\\ spaces" cmd))))) + +(ert-deftest test-video-audio-recording--build-audio-command-boundary-filename-quoted () + "Boundary: output filename with spaces is shell-quoted." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-audio-command + "mic" "sys" "/tmp/my recording.m4a"))) + (should (string-match-p "my\\\\ recording\\.m4a" cmd))))) + +(ert-deftest test-video-audio-recording--build-audio-command-boundary-zero-volume () + "Boundary: zero volume values produce 0.0 in the command." + (let ((cj/recording-mic-boost 0.0) + (cj/recording-system-volume 0.0)) + (let ((cmd (cj/recording--build-audio-command "mic" "sys" "/tmp/out.m4a"))) + (should (string-match-p "volume=0\\.0" cmd))))) + +(provide 'test-video-audio-recording--build-audio-command) +;;; test-video-audio-recording--build-audio-command.el ends here diff --git a/tests/test-video-audio-recording--build-video-command.el b/tests/test-video-audio-recording--build-video-command.el index 7f54f053..3b79c9ec 100644 --- a/tests/test-video-audio-recording--build-video-command.el +++ b/tests/test-video-audio-recording--build-video-command.el @@ -76,6 +76,22 @@ ;; Filename should be quoted/escaped (should (string-match-p "recording" cmd)))))) +(ert-deftest test-video-audio-recording--build-video-command-boundary-x11-device-quoted () + "Boundary: X11 device names with spaces are shell-quoted." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-video-command + "device with spaces" "sys" "/tmp/out.mkv" nil))) + (should (string-match-p "device\\\\ with\\\\ spaces" cmd))))) + +(ert-deftest test-video-audio-recording--build-video-command-boundary-x11-filename-quoted () + "Boundary: X11 output filename with spaces is shell-quoted." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-video-command + "mic" "sys" "/tmp/my recording.mkv" nil))) + (should (string-match-p "my\\\\ recording\\.mkv" cmd))))) + (ert-deftest test-video-audio-recording--build-video-command-boundary-zero-volume () "Zero volume values produce 0.0 in the command." (let ((cj/recording-mic-boost 0.0) |
