Skip to content

iOS/JSC: getUserMedia camera VideoTexture never renders — NativeVideo::SetSrcObject rejects MediaStream (InstanceOf fails across napi/JSC) #1768

Description

@julapy

Summary

On iOS (JavaScriptCore), NativeVideo::SetSrcObject rejects the MediaStream returned by navigator.mediaDevices.getUserMedia, so VideoTexture.CreateFromStreamAsync never renders the camera feed. Camera capture works (AVCaptureSession runs, frames flow), but no texture ever updates.

Environment

  • Babylon Native master @ 64f545de; Babylon.js 9.9.1 (UMD babylon.max.js)
  • iOS 26.5, iPhone 15 Pro Max (device), Xcode 26
  • Repro: Apps/Playground iOS with the cameraTexture path in experience.js enabled

Root cause

Plugins/NativeCamera/Source/NativeVideo.cpp, SetSrcObject:

if (value.IsNull() || value.IsUndefined() ||
    !value.As<Napi::Object>().InstanceOf(MediaStream::GetConstructor(env)))
{
    // reject: m_streamObject cleared, m_isReady = false
    return;
}

getUserMedia creates the stream via MediaStream::GetConstructor(env).New({}), and SetSrcObject checks InstanceOf(MediaStream::GetConstructor(env)) against the same cached constructor — yet InstanceOf returns false across the napi/JavaScriptCore boundary for the genuine MediaStream object.

On-device trace (instrumented):

SetSrcObject called null=0 obj=1 instanceOfMediaStream=0   <- object is a MediaStream, InstanceOf fails

Because the stream is rejected, readyState stays 0, so Babylon.js's VideoTexture.CreateFromStreamAsync never gets the "playing" event (its NativeVideo::Play only fires "playing" when a stream is set), the returned Promise never resolves, the VideoTexture is never constructed, and engine.updateVideoTexture is never called.

Impact

getUserMedia + VideoTexture.CreateFromStreamAsync (camera-to-texture) is completely broken on the JSC/iOS backend.

Suggested fix

Either investigate why Napi::Value::InstanceOf fails for ObjectWrap constructors on the JSC engine, or identify the MediaStream more robustly. A pragmatic fix is to duck-type on the presence of getVideoTracks:

const bool looksLikeMediaStream =
    value.IsObject() && value.As<Napi::Object>().Get("getVideoTracks").IsFunction();
if (value.IsNull() || value.IsUndefined() || !looksLikeMediaStream) { /* reject */ }

Happy to open a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions