Prefer a known device ID over the stripped-label match

Resolve the microphone by exact label, then by a device ID that names a
real capture device, and only then by the label without Chromium's
transport suffix, so the name heuristic cannot override a precise
identifier.

Move the matching into a pure function and replace the source-text
assertions with a test that compiles it with swiftc and checks the
results against a fixed device list, following the lifecycle test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sourabhxyzandClaude Opus 5.5 committed 2026-10-15 12:16:56 +05:30
1 parent e029d11663
commit 13edd56a44
3 files changed
+134 -40

No files matched your search

@@ -0,0 +1,100 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
// Compile the production matching functions and run them against a fixed
// device list, so no real microphone or permission prompt is involved.
describe.skipIf(process.platform !== "darwin")("recorder microphone selection", () => {
let directory: string;
let executable: string;
beforeAll(() => {
directory = mkdtempSync(join(tmpdir(), "recordly-microphone-"));
executable = join(directory, "matcher");
const source = readFileSync(
new URL("./ScreenCaptureKitRecorder.swift", import.meta.url),
"utf8",
);
const matching = source.slice(
source.indexOf("private static func matchMicrophoneDeviceID"),
source.indexOf("private func supportsNativeMicrophoneCapture"),
);
writeFileSync(
join(directory, "main.swift"),
`
import Foundation
enum Matcher {
${matching}
}
extension Matcher {
static func run(_ arguments: [String]) -> String? {
switch arguments[0] {
case "undecorated":
return undecoratedMicrophoneName(forLabel: arguments[1])
default:
return matchMicrophoneDeviceID(
label: arguments[1],
deviceId: arguments[2],
devices: [
(name: "Shure MV7", uniqueID: "usb-mv7"),
(name: "MacBook Pro Microphone", uniqueID: "built-in"),
(name: "Foo", uniqueID: "foo"),
(name: "Foo (Pro)", uniqueID: "foo-pro"),
]
)
}
}
}
print(Matcher.run(Array(CommandLine.arguments.dropFirst())) ?? "<none>")
`,
);
const build = spawnSync(
"swiftc",
[
"-module-cache-path",
join(directory, "cache"),
join(directory, "main.swift"),
"-o",
executable,
],
{ encoding: "utf8", timeout: 60_000 },
);
expect(build.status, build.stderr).toBe(0);
}, 60_000);
afterAll(() => {
if (directory) rmSync(directory, { recursive: true, force: true });
});
const run = (...args: string[]) => {
const result = spawnSync(executable, args, { encoding: "utf8", timeout: 5_000 });
expect(result.status, result.stderr).toBe(0);
return result.stdout.trim();
};
it.each([
["Shure MV7 (14ed:1012)", "Shure MV7"],
["Mic (Built-in)", "Mic"],
["Name (A) (B)", "Name (A)"],
["(Only)", "<none>"],
["Plain", "<none>"],
["Mic (unclosed", "<none>"],
["", "<none>"],
])("strips the Chromium suffix from %j", (label, expected) => {
expect(run("undecorated", label)).toBe(expected);
});
it.each([
// Chromium's deviceId is a per-origin hash that never matches a uniqueID.
["Shure MV7 (14ed:1012)", "d4488ddaa3f0f10c", "usb-mv7"],
["Foo (Pro)", "", "foo-pro"],
["Shure MV7", "built-in", "usb-mv7"],
["Foo (USB)", "built-in", "built-in"],
["Foo (USB)", "", "foo"],
["Default - MacBook Pro Microphone (Built-in)", "default", "<none>"],
["Unknown Mic (Virtual)", "", "<none>"],
["", "", "<none>"],
])("resolves label %j with device ID %j to %j", (label, deviceId, expected) => {
expect(run("match", label, deviceId)).toBe(expected);
});
});
+34 -20
View File
@@ -855,20 +855,37 @@ final class ScreenCaptureRecorder: NSObject, SCStreamOutput, SCStreamDelegate {
}
private static func resolveMicrophoneCaptureDeviceID(config: CaptureConfig) -> String? {
let audioDevices = AVCaptureDevice.devices(for: .audio)
let audioDevices = AVCaptureDevice.devices(for: .audio).map {
(name: $0.localizedName, uniqueID: $0.uniqueID)
}
return matchMicrophoneDeviceID(
label: config.microphoneLabel,
deviceId: config.microphoneDeviceId,
devices: audioDevices
)
}
if let microphoneLabel = config.microphoneLabel?.trimmingCharacters(in: .whitespacesAndNewlines), !microphoneLabel.isEmpty {
for candidateName in microphoneNameCandidates(forLabel: microphoneLabel) {
if let matchedDevice = audioDevices.first(where: { $0.localizedName == candidateName }) {
return matchedDevice.uniqueID
}
}
/// Matches the exact label first, then a known device ID, and only then the
/// label without its transport suffix, because that last step is a name
/// heuristic and must not override a precise identifier.
private static func matchMicrophoneDeviceID(
label: String?,
deviceId: String?,
devices: [(name: String, uniqueID: String)]
) -> String? {
let label = label?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !label.isEmpty, let matchedDevice = devices.first(where: { $0.name == label }) {
return matchedDevice.uniqueID
}
if let microphoneDeviceId = config.microphoneDeviceId?.trimmingCharacters(in: .whitespacesAndNewlines), !microphoneDeviceId.isEmpty {
if audioDevices.contains(where: { $0.uniqueID == microphoneDeviceId }) {
return microphoneDeviceId
}
let deviceId = deviceId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !deviceId.isEmpty, devices.contains(where: { $0.uniqueID == deviceId }) {
return deviceId
}
if let undecoratedName = undecoratedMicrophoneName(forLabel: label),
let matchedDevice = devices.first(where: { $0.name == undecoratedName }) {
return matchedDevice.uniqueID
}
return nil
@@ -877,17 +894,14 @@ final class ScreenCaptureRecorder: NSObject, SCStreamOutput, SCStreamDelegate {
/// The label comes from Chromium's `enumerateDevices()`, which appends a
/// transport suffix to the CoreAudio name — "Shure MV7 (14ed:1012)",
/// "MacBook Pro Microphone (Built-in)", "BlackHole 16ch (Virtual)" — so it
/// rarely equals `localizedName` as-is. Without the undecorated candidate the
/// rarely equals `localizedName` as-is. Without the undecorated name the
/// selection is silently ignored and the system default input is recorded.
private static func microphoneNameCandidates(forLabel label: String) -> [String] {
var candidates = [label]
if label.hasSuffix(")"), let suffixStart = label.range(of: " (", options: .backwards) {
let undecoratedName = label[..<suffixStart.lowerBound].trimmingCharacters(in: .whitespaces)
if !undecoratedName.isEmpty {
candidates.append(undecoratedName)
}
private static func undecoratedMicrophoneName(forLabel label: String) -> String? {
guard label.hasSuffix(")"), let suffixStart = label.range(of: " (", options: .backwards) else {
return nil
}
return candidates
let undecoratedName = label[..<suffixStart.lowerBound].trimmingCharacters(in: .whitespaces)
return undecoratedName.isEmpty ? nil : undecoratedName
}
private func supportsNativeMicrophoneCapture(streamConfig: SCStreamConfiguration) -> Bool {
@@ -117,23 +117,3 @@ describe("ScreenCaptureKitRecorder first frame timing", () => {
expect(audioGuard).toBeLessThan(callback.indexOf("if outputType == .audio"));
});
});
describe("ScreenCaptureKitRecorder microphone selection", () => {
const resolver = recorderSource.slice(
recorderSource.indexOf("private static func resolveMicrophoneCaptureDeviceID"),
recorderSource.indexOf("private func supportsNativeMicrophoneCapture"),
);
it("matches the selected microphone by its undecorated Chromium label", () => {
// Chromium reports "Shure MV7 (14ed:1012)"; CoreAudio names it "Shure MV7".
expect(resolver).toContain(
"for candidateName in microphoneNameCandidates(forLabel: microphoneLabel)",
);
expect(resolver).toContain("$0.localizedName == candidateName");
expect(resolver).toContain('label.range(of: " (", options: .backwards)');
});
it("prefers the raw label so device names ending in parentheses still match", () => {
expect(resolver).toContain("var candidates = [label]");
});
});