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.
Summary
On iOS (JavaScriptCore),
NativeVideo::SetSrcObjectrejects theMediaStreamreturned bynavigator.mediaDevices.getUserMedia, soVideoTexture.CreateFromStreamAsyncnever renders the camera feed. Camera capture works (AVCaptureSession runs, frames flow), but no texture ever updates.Environment
master@64f545de; Babylon.js9.9.1(UMDbabylon.max.js)Apps/PlaygroundiOS with thecameraTexturepath inexperience.jsenabledRoot cause
Plugins/NativeCamera/Source/NativeVideo.cpp,SetSrcObject:getUserMediacreates the stream viaMediaStream::GetConstructor(env).New({}), andSetSrcObjectchecksInstanceOf(MediaStream::GetConstructor(env))against the same cached constructor — yetInstanceOfreturns false across the napi/JavaScriptCore boundary for the genuineMediaStreamobject.On-device trace (instrumented):
Because the stream is rejected,
readyStatestays0, so Babylon.js'sVideoTexture.CreateFromStreamAsyncnever gets the"playing"event (itsNativeVideo::Playonly fires"playing"when a stream is set), the returned Promise never resolves, theVideoTextureis never constructed, andengine.updateVideoTextureis never called.Impact
getUserMedia+VideoTexture.CreateFromStreamAsync(camera-to-texture) is completely broken on the JSC/iOS backend.Suggested fix
Either investigate why
Napi::Value::InstanceOffails for ObjectWrap constructors on the JSC engine, or identify theMediaStreammore robustly. A pragmatic fix is to duck-type on the presence ofgetVideoTracks:Happy to open a PR.