From e47b4e0a735d9a40f904517e12e0849ef3bd51eb Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:38:56 +0200 Subject: [PATCH] fix(macos): fall back to AVFoundation audio when CoreAudio fails to open On macOS 27 beta, CoreAudio rejects ao_coreaudio's channel-layout setup with paramErr (-50), and since 2.14.0 pinned ao=coreaudio as the only output, the failed init left every video playing with no audio and no selectable audio track. Append avfoundation as the fallback, mirroring upstream mpv's macOS probe order: every format still opens through the HAL-backed CoreAudio path when it works, and the fallback only engages when CoreAudio's init fails outright. close #1964 --- macos/Runner/MpvPlayer/MpvPlayerCore.swift | 12 ++++++++---- macos/RunnerTests/RunnerTests.swift | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/macos/Runner/MpvPlayer/MpvPlayerCore.swift b/macos/Runner/MpvPlayer/MpvPlayerCore.swift index 2dd360385..119c47cbc 100644 --- a/macos/Runner/MpvPlayer/MpvPlayerCore.swift +++ b/macos/Runner/MpvPlayer/MpvPlayerCore.swift @@ -102,10 +102,14 @@ class MpvPlayerCore: MpvPlayerCoreBase { } override func configurePlatformMpvOptions(mpv: OpaquePointer) { - // Keep every macOS format on CoreAudio's HAL-backed path. This deliberately - // gives up AVFoundation spatialization so PCM and compressed streams share - // the same stable output implementation. - checkError(mpv_set_option_string(mpv, "ao", "coreaudio")) + // CoreAudio first: every format normally plays through the one HAL-backed + // timing path, deliberately giving up AVFoundation spatialization. The + // avfoundation fallback mirrors upstream mpv's macOS probe order and only + // engages when CoreAudio's init fails outright — macOS 27 beta rejects + // ao_coreaudio's channel-layout setup with paramErr (-50), and a + // single-entry ao list would turn that into playback with no audio at + // all (#1964). + checkError(mpv_set_option_string(mpv, "ao", "coreaudio,avfoundation")) } func reattachMetalLayer() { diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift index bde419155..5236d998d 100644 --- a/macos/RunnerTests/RunnerTests.swift +++ b/macos/RunnerTests/RunnerTests.swift @@ -128,7 +128,7 @@ final class MpvPlayerContractTests: XCTestCase { XCTAssertFalse(core.isPaused, "The accepted pause write must commit before completion") } - func testMacOSVideoCoreUsesCoreAudioOutput() { + func testMacOSVideoCoreUsesCoreAudioWithAVFoundationFallback() { guard let mpv = mpv_create() else { return XCTFail("mpv_create failed") } @@ -151,7 +151,7 @@ final class MpvPlayerContractTests: XCTestCase { mpv_get_property_string(mpv, $0) } defer { mpv_free(optionValue) } - XCTAssertEqual(optionValue.map { String(cString: $0) }, "coreaudio") + XCTAssertEqual(optionValue.map { String(cString: $0) }, "coreaudio,avfoundation") } func testPauseIntentUpdatesCacheBeforeAsyncWriteCompletes() {