Mac内录系统声音:原理、虚拟声卡与FFmpeg实操

📅 2026/8/26 13:37:03
Mac内录系统声音:原理、虚拟声卡与FFmpeg实操
1. 背景与核心概念在 Mac 上录制系统正在播放的声音是一件看起来简单、实际却很麻烦的事情。Windows 上有一个经典的“立体声混音”选项开启后录音软件可以截获电脑播放出来的所有声音但 macOS 默认没有提供这个方案。很多开发者想录课件、录网课、做视频剪辑音效素材或者采集在线会议回放都会卡在“怎么把 Mac 播放的声音录下来”这一步。DesktopAudio 就是一个典型的内录工具思路。它解决的问题很明确让你把 Mac 当前正在播放的音频保存成文件而不需要拿麦克风对着音箱录音。这样可以避免环境噪音也能保证音频干净、稳定。从项目名看它面向的是“Desktop”——桌面端系统级音频采集而不是某个 App 内部的音频接口。要理解 DesktopAudio 这类工具需要先弄清楚 macOS 的音频处理链路。当用户打开一个视频或者音乐 App声音数据会经过 Core Audio 音频框架发送到当前默认的输出设备。输出设备可能是内建扬声器、USB 耳机、HDMI 显示器也可能是虚拟声卡。普通录音软件直接从“音频输入设备”读取数据而系统“正在播放的声音”是在输出设备上的所以默认情况下录音软件拿不到这些数据。于是问题被转换成如何把“输出设备上的音频数据”转发给“录音软件可以读取的输入设备”。主流的做法有三种安装虚拟声卡驱动创建多输出设备做回环或者使用系统级 API 直接捕获。后面的章节会分别拆开讲。这篇教程适合以下读者想在 Mac 上录制系统内部声音但是不知道从何下手的新手。想在自己的开发工具里集成“系统音频录制”功能的开发者。遇到过“录出来没有声音”“只有麦克风声音”“采样率不匹配”等问题的使用者。读完这篇文章后你可以掌握 Mac 内录的原理、三种常用的实现方式、具体的命令行操作以及常见问题的定位思路。2. 内录原理与方案对比2.1 为什么 macOS 默认不能内录macOS 的音频服务以 Core Audio 为核心。系统在播放声音时音频数据流向输出设备系统在录音时音频数据来自输入设备。这两个方向默认是隔离的。macOS 没有像 Windows 那样在系统设置中直接暴露“录制系统输出”的开关所以 App 默认无法监听系统播放的声音。如果要实现内录核心思路是把“输出”和“输入”打通。最简单的方式就是引入虚拟声卡让系统把播放的音频同时发送到一个虚拟设备这个虚拟设备对录音软件来说又是一个可读取的输入设备。这样就完成了一次回环。从另一个角度看macOS 高版本也提供了系统级 API比如 ScreenCaptureKit。这个 API 可以捕获屏幕画面也能捕获系统音频属于官方支持的解决方案。部分新开发的桌面音频录制工具就是基于这类系统 API 实现的好处是用户不需要安装额外的驱动但需要系统授权。2.2 三种主流方案对比方案是否需要额外驱动系统版本要求能否边听边录使用门槛开发成本虚拟声卡 录音软件是各版本通用需要配合多输出设备中低多输出设备 虚拟声卡是各版本通用可以中低ScreenCaptureKit 系统 API否macOS 12.3推荐 13可以中中AudioUnit 底层开发否老版本也可用需要自行设计混音高高虚拟声卡方案的代表工具有 BlackHole、Loopback、Soundflower 等。它们的核心思路一致只是实现和稳定性不同。ScreenCaptureKit 更适合开发者集成到自己的工具中属于“代码方案”。DesktopAudio 这类工具如果不是使用虚拟声卡大概率就是走 ScreenCaptureKit 或者类似的系统捕获通道。这里要提醒一句无论使用哪种方案录制别人有版权的内容之前请确认你有合法授权。本文只讨论技术实现和个人学习场景下的应用。2.3 核心概念虚拟声卡与多输出设备虚拟声卡是一个没有真实物理接口的音频设备。系统把音频数据发到虚拟声卡后数据会停留在系统内存中等待录音软件读取。BlackHole 是开源项目在开发者中使用率很高因为它免费、稳定且支持 2 声道、16 声道等不同版本。多输出设备则是一种组合设备它把多个输出设备捆绑成一个设备。例如把“扬声器”和“BlackHole 2ch”绑成一个多输出设备并把多输出设备设置为系统默认输出。这样一来扬声器继续播放声音声音同时也会被发送到 BlackHole录音软件就能从 BlackHole 读取到正在播放的音频数据。这类方案在日常使用中最大的优势是“边听边录”你既能听到声音又能录制声音。如果直接把默认输出切到 BlackHole而不做多输出设备系统会无声因为音频全部进了虚拟设备没有送到扬声器。3. 环境准备与版本说明在开始实操之前先说明一下环境。本文的示例以常见的 macOS 环境为例不同版本的细节可能有差异但整体思路是可以通用的。3.1 操作系统macOS 13 Ventura 及以上版本。如果你使用 macOS 12 MontereyScreenCaptureKit 也可以尝试但建议优先确认 API 行为是否符合预期。如果使用更老的 macOS虚拟声卡方案仍然可用兼容性也比较好。3.2 必备工具HomebrewmacOS 上最常用的包管理工具。FFmpeg用于录音、转码、检查音频信息也可以用于视频剪辑场景中的音频处理。Xcode如果需要自己写 Swift 代码建议使用 Xcode 15 或更新版本。音频 MIDI 设置系统自带工具位于“应用程序/实用工具”目录也可以按下 Command 空格后搜索“音频 MIDI 设置”。版本建议brew --version ffmpeg -version执行命令后检查版本是否正常输出。不同版本的 FFmpeg 参数略有差异但本文用到的-f avfoundation参数已经存在较长时间可以放心使用。3.3 权限准备如果使用 ScreenCaptureKit 方案需要进行屏幕录制授权。打开“系统设置 隐私与安全性 屏幕录制”把当前使用的终端工具或 App 加进允许列表。如果不授权ScreenCaptureKit 可能拿不到可捕获内容启动时直接崩溃或者录出来的音频为空。部分内录工具在首次启动时也会申请麦克风权限这是因为系统把音频采集统一归入隐私保护范畴。如果出现权限弹窗请确认工具的来源再决定是否授权。4. 实战一使用虚拟声卡录制 Mac 系统声音下面是一套完整的虚拟声卡内录流程。以 BlackHole 和 FFmpeg 为例整个过程分为三步安装虚拟声卡、创建多输出设备、录制音频。4.1 安装 BlackHole使用 Homebrew 安装是最简单的方式。打开终端执行brew install blackhole-2ch安装完成之后打开“音频 MIDI 设置”如果看到类似BlackHole 2ch的音频设备说明驱动已经加载成功。如果没有出现可以先注销当前用户重新登录再打开查看。不建议直接去修改/System/Library/Extensions目录因为新版本 macOS 对系统目录的管理越来越严格手动复制驱动文件容易造成签名和权限问题。4.2 创建多输出设备目标是让声音同时输出到扬声器和 BlackHole。打开“音频 MIDI 设置”点击左下角的加号选择“创建多输出设备”。在新出现的设备条目中勾选你的物理输出设备和BlackHole 2ch。然后把多输出设备的采样率统一设置为 48000 Hz。点击“系统设置”或“声音”面板把默认输出设备切换为刚创建的多输出设备。此时播放一段音乐你应该能正常听到声音同时音频数据也在进入 BlackHole 设备。如果听不到声音说明多输出设备里没有勾选物理输出设备或者设备没有设为默认输出。这个场景是 macOS 内录中最常用的配置因为系统会同时把音频发给扬声器和虚拟声卡。后续录制时录音软件从虚拟声卡读取数据即可。4.3 使用 FFmpeg 录制先列出系统当前可用的音频输入设备ffmpeg -f avfoundation -list_devices true -i 执行结果中会列出音频设备和视频设备。找到名字里带 BlackHole 的设备记下它的设备名通常类似BlackHole 2ch。然后执行录制命令ffmpeg -f avfoundation -i :BlackHole 2ch -t 60 -c:a aac -b:a 192k output.m4a解释一下参数-f avfoundation指定 macOS 上的音视频采集框架。-i :BlackHole 2ch指定输入设备。冒号前面是视频设备索引留空表示不采集视频冒号后面是音频设备名。-t 60录制 60 秒后自动停止。-c:a aac把音频编码为 AAC 格式。-b:a 192k设置音频码率。不加-t参数时按CtrlC停止录制。录制期间正常播放你想录的声音即可。4.4 验证录制结果录制完成后用 ffprobe 检查文件信息ffprobe output.m4a输出会包含时长、编码格式、采样率、声道数等。例如Duration: 00:01:00.00, start: 0.000000, bitrate: 196 kb/s Stream #0:0: Audio: aac (LC), 48000 Hz, stereo如果看到48000 Hz, stereo说明录制成功。如果文件时长接近 0或者没有音频流说明输入设备选择有误需要回到设备列表里确认 BlackHole 的设备名。5. 实战二自己的最小 Mac 内录工具虚拟声卡方案适合直接使用但对普通用户来说安装驱动和创建多输出设备还是有门槛。如果你想开发一个类似 DesktopAudio 的工具直接嵌入到自己的应用中可以用 ScreenCaptureKit 来捕获系统音频。这种方式不依赖第三方驱动用户只需要完成一次系统授权。下面是一个最小实现的核心代码片段。为了保持代码简洁只展示 ScreenCaptureKit 初始化与音频输出回调的核心部分。5.1 创建 Xcode 工程结构假设工程名为DesktopAudioDemo核心文件如下DesktopAudioDemo/ ├── DesktopAudioDemoApp.swift ├── AudioRecorder.swift └── Info.plist在Info.plist中需要添加屏幕录制权限描述keyNSScreenCaptureUsageDescription/key string捕获系统正在播放的音频时需要屏幕录制权限/string注意ScreenCaptureKit 的权限机制会随着系统版本变化实际开发时请以 Xcode 的授权提示为准。5.2 Swift 核心代码import Foundation import ScreenCaptureKit import AVFoundation MainActor final class AudioRecorder: NSObject, SCStreamOutput { private var stream: SCStream? func startRecording() async throws { // 获取可分享内容包括屏幕、窗口和音频 let content try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true) guard let display content.displays.first else { throw RecorderError.noDisplay } // 捕获整个屏幕不排除任何窗口 let filter SCContentFilter(display: display, excludingWindows: []) // 开启音频捕获并设置采样率和声道数 let config SCStreamConfiguration() config.capturesAudio true config.sampleRate 48000 config.channelCount 2 stream SCStream(filter: filter, configuration: config, delegate: nil) // 注册音频输出回调 try stream?.addStreamOutput(self, type: .audio, sampleHandlerQueue: .main) try await stream?.startCapture() } func stopRecording() async { await stream?.stopCapture() stream nil } // 音频数据回调 nonisolated func stream(_ stream: SCStream, didOutput sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) { guard type .audio else { return } // 这里拿到的是 CMSampleBuffer里面包含 PCM 音频数据 // 实际项目中需要转换为 AVAudioPCMBuffer再写入 AVAudioFile // 或者交给音频引擎做进一步处理 print(收到音频数据) } enum RecorderError: Error { case noDisplay } }这段代码展示了最核心的流程获取当前可分享内容找到一块屏幕。创建SCContentFilter指定捕获范围。创建SCStreamConfiguration打开音频捕获。创建SCStream并启动。通过SCStreamOutput回调拿到音频数据。真正的完整工具还需要在这些音频数据上做编码保存。一般做法是把CMSampleBuffer转换成AVAudioPCMBuffer再写入AVAudioFile。这一步会涉及音频格式转换代码量比较大感兴趣的读者可以查阅 ScreenCaptureKit 和 AVAudioFile 的官方文档。5.3 运行与授权流程在 Xcode 中运行这个工程首次启动时系统会弹出屏幕录制授权提示。你需要到“系统设置 隐私与安全性 屏幕录制”里把当前 App 加进允许列表然后重新运行。如果授权不通过SCShareableContent.excludingDesktopWindows返回的内容可能是空列表或者捕获过程中直接报错。所以工具里一定要对授权失败做明确提示方便用户理解问题。6. 常见问题与排查思路Mac 内录最让人头疼的不是原理而是各种“录出来没有声音”的奇怪现象。下面整理常见的几类问题按排查顺序列出。问题现象常见原因解决思路录出来的文件没有声音系统默认输出没有包含 BlackHole 或多输出设备检查音频 MIDI 设置确认多输出设备已勾选 BlackHole录音只有麦克风声音没有系统声音FFmpeg 选择了物理输入设备而不是虚拟声卡使用 list_devices 查看设备列表改用 BlackHole 设备BlackHole 设备没有出现驱动未加载或系统未重新登录注销重登或重启 macOS确认安装过程没有报错音画不同步采样率不一致把所有相关设备的采样率统一为 48000 Hz音量太小或爆音多输出设备中各设备音量增益不一致在音频 MIDI 设置中调整 Main Volume录到一半声音中断多输出设备中的虚拟设备掉线检查是否有其他软件占用音频设备尝试重启音频服务ScreenCaptureKit 启动崩溃缺少屏幕录制权限在系统设置中授权然后重新运行 App录制的文件只有单声道声道数配置未生效在录制命令或代码中显式设置 channel_count 为 2下面挑两个高频问题展开说明。6.1 为什么已经选了 BlackHole录出来还是空文件最常见的原因是多输出设备没有真正生效。你虽然安装了 BlackHole也在 FFmpeg 里指定了BlackHole 2ch但系统当前的默认输出仍然是扬声器声音并没有被发送到 BlackHole所以录音文件为空。排查顺序打开“音频 MIDI 设置”确认 BlackHole 设备存在。确认“多输出设备”已经勾选了扬声器和 BlackHole。打开“系统设置 声音 输出”确认默认输出选的是多输出设备。播放一段音乐观察多输出设备是否有电平变化。再次录制 10 秒查看文件。很多情况下问题出在“默认输出”没有切换而不是录制命令。6.2 为什么录出来的声音还会夹杂麦克风声音如果你在 FFmpeg 中指定的输入设备是 Mac 内置麦克风那当然会录到环境声音。正确做法是指定 BlackHole 设备。设备名称不要只凭记忆要用ffmpeg -f avfoundation -list_devices true -i 查看确保设备名和系统当前显示的完全一致。如果某个工具的界面里同时显示“内建麦克风”和“BlackHole”要选择 BlackHole 而不是内建麦克风。这是新手最容易踩的坑。7. 最佳实践与工程建议7.1 录制之前先统一采样率采样率不统一会导致录音卡顿、变调、音画不同步等问题。建议在开始录制之前把所有涉及到的设备采样率统一设置为 48000 Hz。在 BlackHole 设备上打开“音频 MIDI 设置”选择 BlackHole 对应的设备把采样率调整为 48000多输出设备的采样率也要保持一致。如果开发自己的内录工具建议把采样率作为可配置项并在界面展示当前设备的实际采样率避免用户疑惑。7.2 选择合理的编码和码率语音、网课、会议录音AAC 128 kbps 到 192 kbps 足够。音乐素材AAC 256 kbps 以上或者录制 WAV 后无损保存。需要后期剪辑时建议先录成 WAV再统一转成 AAC避免多次转码造成音质损失。FFmpeg 示例# 录制原始音频方便后期剪辑 ffmpeg -f avfoundation -i :BlackHole 2ch -c:a pcm_s16le raw.wav # 后期做响度标准化 ffmpeg -i raw.wav -af loudnormI-16:LRA11:TP-1.5 -c:a aac output.m4a7.3 权限最小化无论是使用现成工具还是自己开发都要遵循权限最小化原则。自己写的工具只申请必要的权限不要同时申请屏幕录制和麦克风权限。对内录工具生成的音频文件做好访问控制避免敏感音频被其他应用读取。发布到团队或生产环境时要提示用户授权用途并且不绕过系统隐私机制。涉及在线会议、付费音乐、直播内容时请确保录制行为符合平台规则和当地法律。7.4 日志与异常处理内录工具最容易出现的问题是“无声”。所以工程实现上一定要记录关键日志启动时列出所有可用音频设备。记录实际选择的设备名和采样率。记录开始录制和停止录制的时间。捕获到音频数据时记录数据包数量。捕获失败时记录失败原因和系统错误码。有了这些日志大部分问题都可以快速定位。如果用户反馈“录了十分钟全是静音”看到日志里“没有捕获到音频数据包”就能马上知道是设备选择或者权限的问题而不是编码问题。7.5 尽量少依赖内核级驱动虚拟声卡方案功能强大但部分驱动实现会涉及系统级扩展不同 macOS 版本的兼容性差异很大。开发团队如果要做系统内录功能优先考虑系统 API例如 ScreenCaptureKit只有需要兼容老系统或特殊场景时再考虑虚拟声卡。不建议在非测试环境中贸然安装来源不明的驱动因为驱动权限很高一旦出现问题会影响整个系统音频服务。7.6 自动化录制时的注意事项如果需要定时录制可以使用 shell 脚本配合 LaunchAgent。但要注意录制任务会持续占用磁盘和 CPU避免设置过长的定时任务。自动录制前先检查默认输出设备是否还是多输出设备。录制完成后自动生成日志方便追踪任务是否正常执行。一个简单的录制脚本示例#!/bin/bash # record-system-audio.sh set -e DEVICE_NAMEBlackHole 2ch OUTPUT_FILE${1:-output.m4a} ffmpeg -hide_banner -loglevel error \ -f avfoundation -i :$DEVICE_NAME \ -c:a aac -b:a 192k $OUTPUT_FILE给脚本加执行权限后即可运行chmod x record-system-audio.sh ./record-system-audio.sh course.m4a8. 总结与学习路线这篇文章围绕 Mac 系统内录这个主题介绍了 DesktopAudio 这类工具要解决的场景也拆开了三种主流的实现方案虚拟声卡、多输出设备、ScreenCaptureKit。通过实战部分你可以用 BlackHole 和 FFmpeg 完整录制 Mac 正在播放的声音也可以看到自己写一个最小内录工具的 Swift 核心代码。在动手实践时建议你先从虚拟声卡方案开始。它不依赖特定系统版本操作逻辑清晰也最容易排查问题。完成一次“无声成功”的录制之后再去尝试 ScreenCaptureKit就会发现很多概念是相通的。下一步可以继续学习这几个方向Core Audio 的基本架构理解采集、输出、设备枚举。AVAudioEngine 的音频处理流程把捕获到的 PCM 数据编码成音频文件。ScreenCaptureKit 的官方文档了解屏幕与音频捕获的最新能力和限制。FFmpeg 音频滤镜通过 loudnorm、volume 等滤镜改善录音质量。最后分享一个实用习惯拿到任何一个 Mac 内录工具先把采样率调成 48kHz再录一段 10 秒的测试音频用 ffprobe 检查结果。这个习惯能帮你在一分钟内排除八成录音问题。希望这篇文章能帮你顺利录下 Mac 正在播放的声音。