@@ -703,32 +703,6 @@ private ConstraintsMap getUserVideo(ConstraintsMap constraints, MediaStream medi
703703
704704 Log .i (TAG , "getUserMedia(video): " + videoConstraintsMap );
705705
706- // If a camera is already active, reuse its VideoSource instead of opening a second
707- // Camera2 session for the same camera device.
708- //
709- // When the same camera is opened twice from one process, cameraserver "steals" it from
710- // the first client: the first CaptureSession receives onDisconnected(), which internally
711- // calls close() -> stopRepeating() -> cancelRequest(). The camera HAL returns ENOSYS
712- // (-38 / "Function not implemented") for cancelRequest when the pipeline is already being
713- // torn down, and Android's CameraCaptureSessionImpl does not handle that error - it
714- // propagates as CameraAccessException(CAMERA_ERROR=3).
715- //
716- // This is reproducible on stock AOSP (Pixel) and any Android device: it is a bug in the
717- // Android Camera2 framework (CameraCaptureSessionImpl.onDisconnected does not guard
718- // against ENOSYS from cancelRequest), not an OEM-specific issue.
719- // NOTE: reuses the first active primary camera entry regardless of camera ID (front/back).
720- // This is intentional for the current use case where all calls share the same camera.
721- // If a future use case requires different calls to use different cameras simultaneously,
722- // add a cameraName check here before reusing.
723- for (Map .Entry <String , VideoCapturerInfoEx > entry : mVideoCapturers .entrySet ()) {
724- VideoCapturerInfoEx existing = entry .getValue ();
725- if (!existing .isScreenCapture && existing .primaryTrackId == null && existing .videoSource != null ) {
726- Log .w (TAG , "getUserMedia(video): camera already active (track=" + entry .getKey ()
727- + "), reusing VideoSource to prevent concurrent camera access" );
728- return buildSharedVideoTrack (existing , entry .getKey (), mediaStream );
729- }
730- }
731-
732706 // NOTE: to support Camera2, the device should:
733707 // 1. Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
734708 // 2. all camera support level should greater than LEGACY
@@ -748,6 +722,31 @@ private ConstraintsMap getUserVideo(ConstraintsMap constraints, MediaStream medi
748722 String facingMode = getFacingMode (videoConstraintsMap );
749723 isFacing = facingMode == null || !facingMode .equals ("environment" );
750724 String deviceId = getSourceIdConstraint (videoConstraintsMap );
725+
726+ // If a camera is already active for the same facing direction, reuse its VideoSource
727+ // instead of opening a second Camera2 session for the same camera device.
728+ //
729+ // When the same camera is opened twice from one process, cameraserver "steals" it from
730+ // the first client: the first CaptureSession receives onDisconnected(), which internally
731+ // calls close() -> stopRepeating() -> cancelRequest(). The camera HAL returns ENOSYS
732+ // (-38 / "Function not implemented") for cancelRequest when the pipeline is already being
733+ // torn down, and Android's CameraCaptureSessionImpl does not handle that error - it
734+ // propagates as CameraAccessException(CAMERA_ERROR=3).
735+ //
736+ // This is reproducible on stock AOSP (Pixel) and any Android device: it is a bug in the
737+ // Android Camera2 framework (CameraCaptureSessionImpl.onDisconnected does not guard
738+ // against ENOSYS from cancelRequest), not an OEM-specific issue.
739+ final boolean requestedFacingFront = isFacing ;
740+ for (Map .Entry <String , VideoCapturerInfoEx > entry : mVideoCapturers .entrySet ()) {
741+ VideoCapturerInfoEx existing = entry .getValue ();
742+ if (!existing .isScreenCapture && existing .primaryTrackId == null
743+ && existing .videoSource != null && existing .cameraName != null
744+ && cameraEnumerator .isFrontFacing (existing .cameraName ) == requestedFacingFront ) {
745+ Log .w (TAG , "getUserMedia(video): camera already active (track=" + entry .getKey ()
746+ + "), reusing VideoSource to prevent concurrent camera access" );
747+ return buildSharedVideoTrack (existing , entry .getKey (), mediaStream );
748+ }
749+ }
751750 CameraEventsHandler cameraEventsHandler = new CameraEventsHandler ();
752751 Pair <String , VideoCapturer > result = createVideoCapturer (cameraEnumerator , isFacing , deviceId , cameraEventsHandler );
753752
0 commit comments