Skip to content

fix streaming crash when canvas video pipeline isn't initialized#1710

Merged
summeroff merged 3 commits into
stagingfrom
fix_streaming_crash_no_video
May 27, 2026
Merged

fix streaming crash when canvas video pipeline isn't initialized#1710
summeroff merged 3 commits into
stagingfrom
fix_streaming_crash_no_video

Conversation

@summeroff
Copy link
Copy Markdown
Contributor

@summeroff summeroff commented May 26, 2026

Summary

Fixes the Sentry-reported EXCEPTION_ACCESS_VIOLATION_READ at osn::SimpleStreaming::UpdateEncoders (osn-simple-streaming.cpp:302), and the equivalent latent crash in AdvancedStreaming::UpdateEncoders (osn-advanced-streaming.cpp:391).

Root cause

obs_get_video() unconditionally dereferences obs->data.main_canvas->mix->video. When obs_reset_video fails at app startup (e.g. when D3D11 returns DXGI_ERROR_DEVICE_REMOVED / DXGI_ERROR_DEVICE_HUNG during initial texture creation), obs_canvas_clear_mix has already set canvas->mix = NULL, and the failed obs_create_video_mix leaves it that way. On Go-Live, UpdateEncoders calls obs_get_video() and crashes.

The original Sentry report's OBS log shows the textbook sequence — 4× failed SetVideoContext with result: -1 and Device Removed Reason: 887A0006 — followed immediately by the SimpleStreaming::Create … Start chain.

Changes

  • osn-simple-streaming.cpp / osn-advanced-streaming.cpp:
    • UpdateEncoders switches from obs_get_video() (main-canvas, unguarded) to obs_video_mix_get(this->GetCanvas(), mode) + obs_video_mix_get_video(mix) — canvas-scoped and NULL-safe. Falls back to VIDEO_FORMAT_NV12 when video is unavailable, which the existing switch already treats as the default.
    • Start gains a defense-in-depth guard: if the streaming canvas has no video mix, return ErrorCode::CriticalError with a clear message rather than letting UpdateEncoders get there.
  • obs-studio-client/source/video.cpp:
    • osn::Video::set now calls ValidateResponse after the SetVideoContext IPC call. Previously the server returned ErrorCode::Error on failure but the client dropped it on the floor, leaving JS with no way to know the video context never came up. Pairs with the desktop-side change below.

Pairs with

Deferred / out of scope

  • nodeobs_service.cpp:2222 and nodeobs_autoconfig.cpp:1072 also call unguarded obs_get_video() — same pattern, separate audit.
  • Automatic device_rebuild() on SetVideoContext failure would actually recover from transient device-removed; tracked as a follow-up.

Test plan

  • obs-studio-server and obs-studio-client both build clean on Windows (cmake RelWithDebInfo, exit 0).
  • Smoke test SimpleStreaming Start/Stop on a healthy machine — no regression.
  • Reproduce the device-removed path (TDR via NVIDIA Nsight or unplug/replug primary GPU mid-init) and verify Start returns CriticalError instead of crashing.
  • Verify ValidateResponse change doesn't surface false errors during normal startup flow.

🤖 Generated with Claude Code

obs_get_video() unconditionally dereferences main_canvas->mix->video.
When obs_reset_video fails at startup (e.g. DXGI_ERROR_DEVICE_REMOVED
during initial texture creation), canvas->mix is left NULL and the
next SimpleStreaming::Start crashes here -- seen in Sentry crashing
osn::SimpleStreaming::UpdateEncoders at osn-simple-streaming.cpp:302
with EXCEPTION_ACCESS_VIOLATION_READ.

- Switch SimpleStreaming/AdvancedStreaming UpdateEncoders to
  obs_video_mix_get(GetCanvas(), mode) + obs_video_mix_get_video(mix),
  which is canvas-scoped and NULL-safe.
- Add a NULL-mix guard in both Start handlers so we return a clean
  CriticalError instead of crashing if the canvas video never came up.
- Make osn::Video::set propagate IPC errors via ValidateResponse so
  SetVideoContext failures reach the JS layer instead of being
  silently dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The canvas-video lookup in UpdateEncoders / Start was open-coded four
times across SimpleStreaming and AdvancedStreaming. Each site also had
to know that osn::Output::GetCanvas() returns obs_video_info* (the osn
"canvas") rather than libobs's obs_canvas_t -- which is easy to miss
since the libobs API obs_canvas_get_video() takes the latter.

Hide the juggling behind one helper on the Output base class that takes
a rendering mode and returns a video_t* (or NULL when the canvas video
mix isn't ready). Call sites collapse to a single readable line, and
future callers don't have to re-discover the type mismatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens streaming start/update logic against cases where the OBS video pipeline (canvas video mix) was not successfully initialized (e.g., GPU/device lost during startup), preventing null-dereference crashes and surfacing IPC errors back to the JS layer.

Changes:

  • Simple/Advanced streaming: replace obs_get_video() usage in UpdateEncoders() with canvas-scoped video lookup and add a start-time guard that returns a CriticalError when no canvas video mix exists.
  • Server: add Output::GetCanvasVideo(mode) helper to centralize canvas→mix→video lookup.
  • Client: propagate SetVideoContext IPC failures via ValidateResponse so initialization errors are observable in JS.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
obs-studio-server/source/osn-simple-streaming.cpp Use canvas-scoped video output in UpdateEncoders(); fail fast in Start() when no video mix exists.
obs-studio-server/source/osn-advanced-streaming.cpp Same crash-avoidance pattern as SimpleStreaming for advanced mode.
obs-studio-server/source/osn-output.hpp Declare Output::GetCanvasVideo() helper API.
obs-studio-server/source/osn-output.cpp Implement Output::GetCanvasVideo() helper.
obs-studio-client/source/video.cpp Add ValidateResponse handling for SetVideoContext IPC call.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread obs-studio-server/source/osn-output.cpp Outdated

video_t *osn::Output::GetCanvasVideo(obs_video_rendering_mode mode)
{
return obs_video_mix_get_video(obs_video_mix_get(m_canvas, mode));
Match the pattern in osn-video.cpp: return nullptr when the mix
lookup fails instead of dereferencing it. Without this, the helper
crashes in exactly the scenario this PR targets (no canvas video
mix), defeating the Start() guards that call it. Also apply
clang-format-13 fixes flagged by CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@summeroff summeroff merged commit e308a1b into staging May 27, 2026
19 checks passed
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