Skip to content

macOS arm64 support (vanilla libobs 31.0.3)#26

Open
yuripiratello wants to merge 1 commit into
aza547:mainfrom
yuripiratello:feat/macos-port
Open

macOS arm64 support (vanilla libobs 31.0.3)#26
yuripiratello wants to merge 1 commit into
aza547:mainfrom
yuripiratello:feat/macos-port

Conversation

@yuripiratello

Copy link
Copy Markdown

Adds macOS arm64 support to noobs by porting the platform-specific code (obs_interface_mac.mm) and building vanilla libobs 31.0.3 + a small plugin set from the upstream obsproject/obs-studio source. No Streamlabs OSN.

Companion PR on wow-recorder: aza547/wow-recorder#819

What this gives you

  • obs_interface_mac.mm — Mac equivalents of the Win initPreview / configurePreview / showPreview / hidePreview / disablePreview. Renders the canvas in a borderless, mouse-transparent child NSWindow attached via addChildWindow:ordered:NSWindowAbove. Same conceptual split as node-window-rendering does on Win — separate window for the OBS canvas, BrowserWindow stays opaque, DOM events flow through to the renderer.
  • obs_interface.cpplistSceneItems() wraps obs_scene_enum_items so consumers can hit-test scene items (used by the WCR scene editor on Mac). setDrawSourceOutline already paints orange outlines + corner handles for selected items.
  • dist.js — Mac branch vendors the built libobs.framework + plugin bundles + libobs-opengl.dylib + obs-ffmpeg-mux + libav* dylibs into dist/Frameworks and dist/PlugIns. Patches @rpath on the libobs binary and on every .plugin so dyld resolves the sibling dylibs at runtime.
  • scripts/build-libobs-mac.sh — clones obsproject/obs-studio at the pinned tag, configures CMake with the macos preset (no UI / browser / VLC / WebRTC / DeckLink / scripting), builds the plugin set we need (obs-x264, obs-ffmpeg, mac-capture, mac-videotoolbox, image-source, obs-filters), and copies everything into the repo.
  • .github/workflows/release-mac.ymlmacos-14 runner job that runs the build script, packs the tarball as noobs-darwin-arm64-<version>.tgz, attaches it to the GitHub Release on tag push. Pins Xcode 15.2 + versioned MacOSX14.2.sdk so OBS's compilerconfig.cmake regex matches.

Replay buffer

Vanilla libobs has no convert proc (Streamlabs OSN extension), so the Mac code path uses ffmpeg_muxer directly. wow-recorder gates on a new supportsReplayBuffer capability and starts recording at activity time on Mac — no pre-roll, but no buffer-cycling miscuts either. Win behaviour is untouched.

Known limits

  • arm64 only. No universal build, no Intel macOS coverage.
  • game_capture not available on Mac (no DirectX/Vulkan hook equivalent).
  • The destructor-side sources / filters / volmeters map cleanup was changed from range-for-with-erase to iterate-then-clear because libc++ on macOS arm64 fault under PAC on the original pattern. Same pattern applied to volmeter_cb_ctx.

Versioning

The fork uses pre-release tags vX.Y.Z-mac.beta.N so each CI build attaches a separate tarball. Current beta: v0.0.182-mac.beta.5. Happy to align this with upstream's release naming if you'd prefer.

Out of scope

  • Unit / integration tests for the Mac path. Existing test-errors / test-preview are Win-only and stay that way; Mac smoke-testing is via the consuming wow-recorder build.
  • Audio per-app capture (uses sck_audio_capture for system out; finer per-bundle picker would be a follow-up).
  • Codesigning / notarisation. Done by the consumer (electron-builder in wow-recorder); the noobs CI tarball is unsigned by design — the parent .app deep-signs.

🤖 Generated with Claude Code

Adds macOS arm64 to noobs by porting the platform layer in
obs_interface_mac.mm and building vanilla libobs + a small plugin set
from obsproject/obs-studio source. No Streamlabs OSN.

Build:
- scripts/build-libobs-mac.sh clones obs-studio at the pinned tag,
  configures with the macos cmake preset (UI/browser/VLC/WebRTC/DeckLink/
  scripting all OFF), builds obs-x264, obs-ffmpeg, mac-capture,
  mac-videotoolbox, image-source, obs-filters, and copies the framework
  + plugin bundles + libav* runtime dylibs + obs-ffmpeg-mux into the
  repo's Frameworks/, PlugIns/, and data/ trees.
- dist.js handles the Mac side: cp -R for symlink preservation,
  install_name_tool -add_rpath @loader_path/../../.. on libobs and
  @loader_path/../../../../Frameworks on every .plugin binary so dyld
  resolves siblings without the OBS-bundle assumption.
- .github/workflows/release-mac.yml: macos-14 runner, Xcode 15.2 +
  versioned MacOSX14.2.sdk pinned (OBS 31.0.3 compilerconfig regex
  doesn't match the unversioned symlink), runs the build script,
  packs the tarball, attaches to the GitHub Release on tag push.
  Workspace-internal vendor/obs-studio path so actions/cache hits.

Runtime:
- obs_interface_mac.mm hosts the canvas in a borderless transparent
  child NSWindow, addChildWindow:above attached to the parent
  BrowserWindow's NSWindow. setIgnoresMouseEvents:YES so events fall
  through to the renderer's editor overlay. Handle creation lazily
  on first configurePreview because the parent isn't on screen at
  initPreview time (Electron 'ready-to-show' fires later).
- obs_display_resize takes BACKING pixels — multiply by
  backingScaleFactor so retina displays don't render the canvas at
  1/4 size in the bottom-left corner. getPreviewInfo divides back out
  for renderer-side math.
- listSceneItems wraps obs_scene_enum_items so consumers (e.g. WCR's
  scene editor) can hit-test items. Outlines + corner handles already
  drawn by setDrawSourceOutline.
- Destructor cleanup: range-for + erase invalidates libc++ iterators
  on macOS arm64 and segfaults under PAC. Iterate, free, clear() once
  for sources/filters/volmeters/volmeter_cb_ctx.
- disablePreview moved to mac.mm (#ifdef'd out in cpp) so it can do
  setHidden:YES on the child window when leaving the editor — without
  it the child lingers as a dark rect over whatever page replaces the
  editor.

Versioning:
- Pre-release tags vX.Y.Z-mac.beta.N attach a separate tarball per CI
  build for downstream beta testing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aza547

aza547 commented May 9, 2026

Copy link
Copy Markdown
Owner

How can this function without the recorder buffer? Many game modes rely on this to cope with log flush delays? This approach will frequently miss the start of BGs and any non-retail content where you can't rely on the log flushing on the start activity event?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants