Skip to content

Failing harness repro: setOutputSettings({ codec }) crashes (uncatchable) when a targetBitRate is set#4037

Open
radko93 wants to merge 2 commits into
mrousavy:mainfrom
radko93:repro/ios-setoutputsettings-crash
Open

Failing harness repro: setOutputSettings({ codec }) crashes (uncatchable) when a targetBitRate is set#4037
radko93 wants to merge 2 commits into
mrousavy:mainfrom
radko93:repro/ios-setoutputsettings-crash

Conversation

@radko93

@radko93 radko93 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

This PR is a reproduction: it adds a failing harness test only, so the AWS Device Farm run shows the crash. A proposed fix is in the collapsible section — but note it is reasoned, not yet verified (see below).

What

On iOS, videoOutput.setOutputSettings({ codec }) called after a targetBitRate was configured aborts the whole process with an uncatchable Objective-C NSInvalidArgumentException (SIGTRAP). No JS try/catch can prevent it.

Why

HybridCameraVideoOutput.setOutputSettings (ios/.../HybridCameraVideoOutput.swift) does a read-modify-write of the fully-expanded settings dict:

var currentSettings = self.output.outputSettings(for: connection)   // expanded dict
currentSettings[AVVideoCodecKey] = try codec.toAVVideoCodecType()
self.output.setOutputSettings(currentSettings, for: connection)     // writes whole dict back

When targetBitRate is set, setBitRate(...) has already populated the connection, so outputSettings(for:) returns a dict containing AVVideoCompressionPropertiesKey plus width/height/color keys. AVCaptureMovieFileOutput.setOutputSettings:forConnection: rejects the non-codec keys with an ObjC exception that Swift can't catch → abort.

(It doesn't bite without targetBitRate because the expanded dict is then minimal enough.)

Test

Adds an it(...) to apps/simple-camera/__tests__/visioncamera.video.harness.ts: configure a targetBitRate, then call setOutputSettings({ codec: 'h264' }) and expect it to resolve. On current main the harness app crashes rather than the assertion failing cleanly — that crash is the reproduction. I haven't run the harness locally (no signed device build), so the Device Farm run is the verification.

Observed on iPhone18,1 / iOS 26.5.1, com.margelo.camera.video queue.

Proposed fix (reasoned, NOT verified — we worked around it by not calling setOutputSettings)

Don't round-trip the expanded outputSettings(for:). Write only the keys AVCaptureMovieFileOutput accepts — the codec, and the existing compression properties if present (which setBitRate already writes successfully):

var currentSettings = self.output.outputSettings(for: connection)
if let codec = settings.codec {
  if codec.isRawCodec {
    guard self.options.targetBitRate == nil else {
      throw RuntimeError.error(withMessage:
        "Cannot set bit-rate when recording in a RAW codec! (\(codec.stringValue))")
    }
  }
  currentSettings[AVVideoCodecKey] = try codec.toAVVideoCodecType()
}

// Only forward keys AVCaptureMovieFileOutput accepts; the expanded dict's
// width/height/color keys raise an uncatchable NSInvalidArgumentException.
var safeSettings: [String: Any] = [:]
if let codec = currentSettings[AVVideoCodecKey] {
  safeSettings[AVVideoCodecKey] = codec
}
if let compression = currentSettings[AVVideoCompressionPropertiesKey] {
  safeSettings[AVVideoCompressionPropertiesKey] = compression
}
self.output.setOutputSettings(safeSettings, for: connection)

This mirrors what setBitRate already does (it builds a minimal [AVVideoCodecKey, AVVideoCompressionPropertiesKey] dict and ships without crashing). I have not verified this exact patch on a device — our production workaround was simply to stop calling setOutputSettings — so treat it as a starting point for discussion. The maintainer may know the precise set of keys AVCaptureMovieFileOutput permits.

…n a targetBitRate is set

Calling `videoOutput.setOutputSettings({ codec })` after `targetBitRate` was
configured aborts the process with an uncatchable Objective-C
NSInvalidArgumentException (SIGTRAP).

`HybridCameraVideoOutput.setOutputSettings` reads the fully-expanded
`output.outputSettings(for: connection)` dict and writes it back. When
`targetBitRate` is set, that dict already contains AVVideoCompressionPropertiesKey
plus width/height/color keys. `AVCaptureMovieFileOutput.setOutputSettings:forConnection:`
rejects the non-codec/compression keys, raising an ObjC exception Swift cannot
catch, so the whole app aborts. No JS try/catch can prevent it.

Adds a harness test that configures a targetBitRate, then calls
setOutputSettings({ codec: 'h264' }) and expects it to resolve without crashing.
Expected to crash the harness app on current main.
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

@radko93 is attempting to deploy a commit to the Margelo Team on Vercel.

A member of the Team first needs to authorize it.

@radko93 radko93 marked this pull request as ready for review June 22, 2026 19:09
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