aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/video-audio-recording.el44
-rw-r--r--tests/test-video-audio-recording--build-audio-command.el76
-rw-r--r--tests/test-video-audio-recording--build-video-command.el16
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)