diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-12 11:47:26 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-12 11:47:26 -0500 |
| commit | 092304d9e0ccc37cc0ddaa9b136457e56a1cac20 (patch) | |
| tree | ea81999b8442246c978b364dd90e8c752af50db5 /modules/video-audio-recording.el | |
changing repositories
Diffstat (limited to 'modules/video-audio-recording.el')
| -rw-r--r-- | modules/video-audio-recording.el | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/modules/video-audio-recording.el b/modules/video-audio-recording.el new file mode 100644 index 00000000..e2238949 --- /dev/null +++ b/modules/video-audio-recording.el @@ -0,0 +1,184 @@ +;;; video-audio-recording.el --- Video and Audio Recording -*- lexical-binding: t; coding: utf-8; -*- +;; author: Craig Jennings <c@cjennings.net> +;; +;;; Commentary: +;; Use ffmpeg to record desktop video or just audio. +;; with audio from mic and audio from default audio sink +;; Also supports audio-only recording in Opus format. +;; +;; Note: video-recordings-dir and audio-recordings-dir are defined +;; (and directory created) in user-constants.el +;; +;; +;; To adjust volumes: +;; - Use =M-x cj/recording-adjust-volumes= (or your keybinding =r l=) +;; - Or customize permanently: =M-x customize-group RET cj-recording RET= +;; - Or in your config: +;; #+begin_src emacs-lisp +;; (setq cj/recording-mic-boost 1.5) ; 50% louder +;; (setq cj/recording-system-volume 0.7) ; 30% quieter +;; +;;; Code: + +(require 'user-constants) + +(defgroup cj-recording nil + "Settings for video and audio recording." + :group 'multimedia) + +(defcustom 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)." + :type 'number + :group 'cj-recording) + +(defcustom 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)." + :type 'number + :group 'cj-recording) + +(defvar cj/video-recording-ffmpeg-process nil + "Variable to store the process of the ffmpeg video recording.") + +(defvar cj/audio-recording-ffmpeg-process nil + "Variable to store the process of the ffmpeg audio recording.") + +(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'." + (interactive "P") + (let* ((location (if arg + (read-directory-name "Enter recording location: ") + video-recordings-dir)) + (directory (file-name-directory location))) + (unless (file-directory-p directory) + (make-directory directory t)) + (cj/ffmpeg-record-video location))) + +(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'." + (interactive "P") + (let* ((location (if arg + (read-directory-name "Enter recording location: ") + video-recordings-dir)) + (directory (file-name-directory location))) + (unless (file-directory-p directory) + (make-directory directory t)) + (cj/ffmpeg-record-audio location))) + +(defun cj/ffmpeg-record-video (directory) + "Start an ffmpeg video recording. Save output to DIRECTORY." + (unless cj/video-recording-ffmpeg-process + (let* ((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 " + "-ac 1 " + "-f pulse -i " + "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor " + "-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") + cj/recording-mic-boost + cj/recording-system-volume + filename))) + ;; start the recording + (setq cj/video-recording-ffmpeg-process + (start-process-shell-command "ffmpeg-video-recording" + "*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)))) + +(defun cj/ffmpeg-record-audio (directory) + "Start an ffmpeg audio recording. Save output to DIRECTORY." + (unless cj/audio-recording-ffmpeg-process + (let* ((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 " + "-ac 1 " + "-f pulse -i " + "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor " + "-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") + cj/recording-mic-boost + cj/recording-system-volume + filename))) + ;; start the recording + (setq cj/audio-recording-ffmpeg-process + (start-process-shell-command "ffmpeg-audio-recording" + "*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)))) + +(defun cj/video-recording-stop () + "Stop the ffmpeg video recording process." + (interactive) + (if cj/video-recording-ffmpeg-process + (progn + ;; 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) + (setq cj/video-recording-ffmpeg-process nil) + (message "Stopped video recording.")) + (message "No video recording in progress."))) + +(defun cj/audio-recording-stop () + "Stop the ffmpeg audio recording process." + (interactive) + (if cj/audio-recording-ffmpeg-process + (progn + ;; 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) + (setq cj/audio-recording-ffmpeg-process nil) + (message "Stopped audio recording.")) + (message "No audio recording in progress."))) + +(defun cj/recording-adjust-volumes () + "Interactively adjust recording volume levels." + (interactive) + (let ((mic (read-number "Microphone boost (1.0 = normal, 2.0 = double): " + 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) + (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 + +(provide 'video-audio-recording) +;;; video-audio-recording.el ends here. |
