diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-20 16:54:46 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-20 16:54:46 -0500 |
| commit | f634a04841001ce2535d0a418241636b3c77a323 (patch) | |
| tree | 4810e36816347821ca9e1be3684b09e5b0004b3e | |
| parent | 626c124561114711d37954f911904462325c1f5d (diff) | |
refactor:recording:: improve ffmpeg-based device detection + perf
- Simplified variable declarations by using defvar instead of defcustom.
- Added functions to auto-detect audio devices using PulseAudio
- Enhanced ffmpeg command construction to include detected device
names and optimized process start and stop messages.
- Adjusted process interruption timing for better file finalization.
- Autoload settings and key bindings have also been restructured.
| -rw-r--r-- | modules/video-audio-recording.el | 150 |
1 files changed, 101 insertions, 49 deletions
diff --git a/modules/video-audio-recording.el b/modules/video-audio-recording.el index e2238949..711e903c 100644 --- a/modules/video-audio-recording.el +++ b/modules/video-audio-recording.el @@ -20,25 +20,26 @@ ;; ;;; Code: -(require 'user-constants) +;; Forward declarations +(eval-when-compile (defvar video-recordings-dir)) +(eval-when-compile (defvar audio-recordings-dir)) +(defvar cj/custom-keymap) -(defgroup cj-recording nil - "Settings for video and audio recording." - :group 'multimedia) - -(defcustom cj/recording-mic-boost 2.0 +(defvar cj/recording-mic-boost 2.0 "Volume multiplier for microphone in recordings. +1.0 = normal volume, 2.0 = double volume (+6dB), 0.5 = half volume (-6dB).") -1.0 = normal volume, 2.0 = double volume (+6dB), 0.5 = half volume (-6dB)." - :type 'number - :group 'cj-recording) - -(defcustom cj/recording-system-volume 0.5 +(defvar cj/recording-system-volume 0.5 "Volume multiplier for system audio in recordings. +1.0 = normal volume, 2.0 = double volume (+6dB), 0.5 = half volume (-6dB).") + +(defvar cj/recording-mic-device nil + "PulseAudio device name for microphone input. +If nil, will auto-detect on first use.") -1.0 = normal volume, 2.0 = double volume (+6dB), 0.5 = half volume (-6dB)." - :type 'number - :group 'cj-recording) +(defvar cj/recording-system-device nil + "PulseAudio device name for system audio monitor. +If nil, will auto-detect on first use.") (defvar cj/video-recording-ffmpeg-process nil "Variable to store the process of the ffmpeg video recording.") @@ -46,11 +47,48 @@ (defvar cj/audio-recording-ffmpeg-process nil "Variable to store the process of the ffmpeg audio recording.") +(defun cj/recording-check-ffmpeg () + "Check if ffmpeg is available. +Return t if found, nil otherwise." + (unless (executable-find "ffmpeg") + (user-error "Ffmpeg not found. Install with: sudo pacman -S ffmpeg") + nil) + t) + +(defun cj/recording-detect-mic-device () + "Auto-detect PulseAudio microphone input device. +Returns device name or nil if not found." + (let ((output (shell-command-to-string "pactl list sources short 2>/dev/null"))) + (when (string-match "\\([^\t\n]+\\).*analog.*stereo" output) + (match-string 1 output)))) + +(defun cj/recording-detect-system-device () + "Auto-detect PulseAudio system audio monitor device. +Returns device name or nil if not found." + (let ((output (shell-command-to-string "pactl list sources short 2>/dev/null"))) + (when (string-match "\\([^\t\n]+\\.monitor\\)" output) + (match-string 1 output)))) + +(defun cj/recording-get-devices () + "Get or auto-detect audio devices. +Returns (mic-device . system-device) or nil on error." + ;; Auto-detect if not already set + (unless cj/recording-mic-device + (setq cj/recording-mic-device (cj/recording-detect-mic-device))) + (unless cj/recording-system-device + (setq cj/recording-system-device (cj/recording-detect-system-device))) + + ;; Validate devices + (unless (and cj/recording-mic-device cj/recording-system-device) + (user-error "Could not detect audio devices. Set cj/recording-mic-device and cj/recording-system-device manually")) + + (cons cj/recording-mic-device cj/recording-system-device)) + +;;;###autoload (defun cj/video-recording-start (arg) - "Starts the ffmpeg video recording. - -If called with a prefix arg C-u, choose the location on where to save the -recording, otherwise use the default location in =video-recordings-dir'." + "Start the ffmpeg video recording. +With prefix ARG, prompt for recording location. +Otherwise use the default location in `video-recordings-dir'." (interactive "P") (let* ((location (if arg (read-directory-name "Enter recording location: ") @@ -60,15 +98,15 @@ recording, otherwise use the default location in =video-recordings-dir'." (make-directory directory t)) (cj/ffmpeg-record-video location))) +;;;###autoload (defun cj/audio-recording-start (arg) - "Starts the ffmpeg audio recording. - -If called with a prefix arg C-u, choose the location on where to save the -recording, otherwise use the default location in =video-recordings-dir'." + "Start the ffmpeg audio recording. +With prefix ARG, prompt for recording location. +Otherwise use the default location in `audio-recordings-dir'." (interactive "P") (let* ((location (if arg (read-directory-name "Enter recording location: ") - video-recordings-dir)) + audio-recordings-dir)) (directory (file-name-directory location))) (unless (file-directory-p directory) (make-directory directory t)) @@ -76,21 +114,25 @@ recording, otherwise use the default location in =video-recordings-dir'." (defun cj/ffmpeg-record-video (directory) "Start an ffmpeg video recording. Save output to DIRECTORY." + (cj/recording-check-ffmpeg) (unless cj/video-recording-ffmpeg-process - (let* ((location (expand-file-name directory)) + (let* ((devices (cj/recording-get-devices)) + (mic-device (car devices)) + (system-device (cdr devices)) + (location (expand-file-name directory)) (name (format-time-string "%Y-%m-%d-%H-%M-%S")) (filename (expand-file-name (concat name ".mkv") location)) (ffmpeg-command (format (concat "ffmpeg -framerate 30 -f x11grab -i :0.0+ " - "-f pulse -i " - "alsa_input.pci-0000_00_1b.0.analog-stereo " + "-f pulse -i %s " "-ac 1 " - "-f pulse -i " - "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor " + "-f pulse -i %s " "-ac 2 " "-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 cj/recording-mic-boost cj/recording-system-volume filename))) @@ -100,27 +142,31 @@ recording, otherwise use the default location in =video-recordings-dir'." "*ffmpeg-video-recording*" ffmpeg-command)) (set-process-query-on-exit-flag cj/video-recording-ffmpeg-process nil) - (message "Started video recording process (mic boost: %.1fx, system volume: %.1fx)." - cj/recording-mic-boost cj/recording-system-volume)))) + (message "Started video recording to %s (mic: %.1fx, system: %.1fx)." + filename cj/recording-mic-boost cj/recording-system-volume)))) (defun cj/ffmpeg-record-audio (directory) "Start an ffmpeg audio recording. Save output to DIRECTORY." + (cj/recording-check-ffmpeg) (unless cj/audio-recording-ffmpeg-process - (let* ((location (expand-file-name directory)) + (let* ((devices (cj/recording-get-devices)) + (mic-device (car devices)) + (system-device (cdr devices)) + (location (expand-file-name directory)) (name (format-time-string "%Y-%m-%d-%H-%M-%S")) (filename (expand-file-name (concat name ".opus") location)) (ffmpeg-command (format (concat "ffmpeg " - "-f pulse -i " - "alsa_input.pci-0000_00_1b.0.analog-stereo " + "-f pulse -i %s " "-ac 1 " - "-f pulse -i " - "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor " + "-f pulse -i %s " "-ac 2 " "-filter_complex \"[0:a]volume=%.1f[mic];[1:a]volume=%.1f[sys];[mic][sys]amerge=inputs=2\" " "-c:a libopus " "-b:a 96k " "%s") + mic-device + system-device cj/recording-mic-boost cj/recording-system-volume filename))) @@ -130,9 +176,10 @@ recording, otherwise use the default location in =video-recordings-dir'." "*ffmpeg-audio-recording*" ffmpeg-command)) (set-process-query-on-exit-flag cj/audio-recording-ffmpeg-process nil) - (message "Started audio recording process (mic boost: %.1fx, system volume: %.1fx)." - cj/recording-mic-boost cj/recording-system-volume)))) + (message "Started audio recording to %s (mic: %.1fx, system: %.1fx)." + filename cj/recording-mic-boost cj/recording-system-volume)))) +;;;###autoload (defun cj/video-recording-stop () "Stop the ffmpeg video recording process." (interactive) @@ -141,11 +188,12 @@ recording, otherwise use the default location in =video-recordings-dir'." ;; Use interrupt-process to send SIGINT (graceful termination) (interrupt-process cj/video-recording-ffmpeg-process) ;; Give ffmpeg a moment to finalize the file - (sit-for 1) + (sit-for 0.2) (setq cj/video-recording-ffmpeg-process nil) (message "Stopped video recording.")) (message "No video recording in progress."))) +;;;###autoload (defun cj/audio-recording-stop () "Stop the ffmpeg audio recording process." (interactive) @@ -154,11 +202,12 @@ recording, otherwise use the default location in =video-recordings-dir'." ;; Use interrupt-process to send SIGINT (graceful termination) (interrupt-process cj/audio-recording-ffmpeg-process) ;; Give ffmpeg a moment to finalize the file - (sit-for 1) + (sit-for 0.2) (setq cj/audio-recording-ffmpeg-process nil) (message "Stopped audio recording.")) (message "No audio recording in progress."))) +;;;###autoload (defun cj/recording-adjust-volumes () "Interactively adjust recording volume levels." (interactive) @@ -166,19 +215,22 @@ recording, otherwise use the default location in =video-recordings-dir'." cj/recording-mic-boost)) (sys (read-number "System audio level (1.0 = normal, 0.5 = half): " cj/recording-system-volume))) - (customize-set-variable 'cj/recording-mic-boost mic) - (customize-set-variable 'cj/recording-system-volume sys) + (setq cj/recording-mic-boost mic) + (setq cj/recording-system-volume sys) (message "Recording levels updated - Mic: %.1fx, System: %.1fx" mic sys))) ;; Recording operations prefix and keymap -(define-prefix-command 'cj/record-map nil - "Keymap for video/audio recording operations.") -(define-key cj/custom-keymap "r" 'cj/record-map) -(define-key cj/record-map "V" 'cj/video-recording-stop) -(define-key cj/record-map "v" 'cj/video-recording-start) -(define-key cj/record-map "A" 'cj/audio-recording-stop) -(define-key cj/record-map "a" 'cj/audio-recording-start) -(define-key cj/record-map "l" 'cj/recording-adjust-volumes) ; 'l' for levels +(defvar cj/record-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "V") #'cj/video-recording-stop) + (define-key map (kbd "v") #'cj/video-recording-start) + (define-key map (kbd "A") #'cj/audio-recording-stop) + (define-key map (kbd "a") #'cj/audio-recording-start) + (define-key map (kbd "l") #'cj/recording-adjust-volumes) + map) + "Keymap for video/audio recording operations.") + +;;;###autoload (keymap-set cj/custom-keymap "r" cj/record-map) (provide 'video-audio-recording) ;;; video-audio-recording.el ends here. |
