From 6fc048dd5ea41ca8dec20d00139082c8cf3b8d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20=C5=A0krob=C3=A1k?= Date: Tue, 28 May 2024 16:16:42 +0200 Subject: [PATCH 1/3] Torch support --- .../main/java/org/webrtc/Camera1Capturer.java | 2 +- .../java/org/webrtc/Camera1Enumerator.java | 5 + .../main/java/org/webrtc/Camera2Capturer.java | 4 +- .../java/org/webrtc/Camera2Enumerator.java | 6 ++ .../main/java/org/webrtc/Camera2Session.java | 13 ++- .../main/java/org/webrtc/CameraCapturer.java | 98 ++++++++++++++++++- .../java/org/webrtc/CameraEnumerator.java | 1 + .../java/org/webrtc/CameraVideoCapturer.java | 8 ++ 8 files changed, 128 insertions(+), 9 deletions(-) diff --git a/stream-webrtc-android/src/main/java/org/webrtc/Camera1Capturer.java b/stream-webrtc-android/src/main/java/org/webrtc/Camera1Capturer.java index de172aa1d..009bb2a4f 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/Camera1Capturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/Camera1Capturer.java @@ -26,7 +26,7 @@ public Camera1Capturer( protected void createCameraSession(CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events, Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName, int width, int height, - int framerate) { + int framerate, boolean torch) { Camera1Session.create(createSessionCallback, events, captureToTexture, applicationContext, surfaceTextureHelper, cameraName, width, height, framerate); } diff --git a/stream-webrtc-android/src/main/java/org/webrtc/Camera1Enumerator.java b/stream-webrtc-android/src/main/java/org/webrtc/Camera1Enumerator.java index fb1a21f32..846a2d461 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/Camera1Enumerator.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/Camera1Enumerator.java @@ -63,6 +63,11 @@ public boolean isBackFacing(String deviceName) { return info != null && info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK; } + @Override + public boolean hasTorch(String deviceName) { + return false; //TODO, old API unsupported + } + @Override public List getSupportedFormats(String deviceName) { return getSupportedFormats(getCameraIndex(deviceName)); diff --git a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Capturer.java b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Capturer.java index c4becf481..5c7efcd7c 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Capturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Capturer.java @@ -29,8 +29,8 @@ public Camera2Capturer(Context context, String cameraName, CameraEventsHandler e protected void createCameraSession(CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events, Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName, int width, int height, - int framerate) { + int framerate, boolean torch) { Camera2Session.create(createSessionCallback, events, applicationContext, cameraManager, - surfaceTextureHelper, cameraName, width, height, framerate); + surfaceTextureHelper, cameraName, width, height, framerate, torch); } } diff --git a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Enumerator.java b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Enumerator.java index 456d8cd06..185196428 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Enumerator.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Enumerator.java @@ -65,6 +65,12 @@ public boolean isFrontFacing(String deviceName) { == CameraMetadata.LENS_FACING_FRONT; } + @Override + public boolean hasTorch(String deviceName) { + CameraCharacteristics characteristics = getCameraCharacteristics(deviceName); + return characteristics != null && characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + } + @Override public boolean isBackFacing(String deviceName) { CameraCharacteristics characteristics = getCameraCharacteristics(deviceName); diff --git a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Session.java b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Session.java index dec97a2c2..f13cc311e 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/Camera2Session.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/Camera2Session.java @@ -51,6 +51,8 @@ private static enum SessionState { RUNNING, STOPPED } private final int height; private final int framerate; + private final boolean torch; + // Initialized at start private CameraCharacteristics cameraCharacteristics; private int cameraOrientation; @@ -167,6 +169,10 @@ public void onConfigured(CameraCaptureSession session) { captureRequestBuilder.set( CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false); + if (Camera2Session.this.torch) { + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, 2); + } + chooseStabilizationMode(captureRequestBuilder); chooseFocusMode(captureRequestBuilder); @@ -270,14 +276,14 @@ public void onCaptureFailed( public static void create(CreateSessionCallback callback, Events events, Context applicationContext, CameraManager cameraManager, SurfaceTextureHelper surfaceTextureHelper, String cameraId, int width, int height, - int framerate) { + int framerate, boolean torch) { new Camera2Session(callback, events, applicationContext, cameraManager, surfaceTextureHelper, - cameraId, width, height, framerate); + cameraId, width, height, framerate, torch); } private Camera2Session(CreateSessionCallback callback, Events events, Context applicationContext, CameraManager cameraManager, SurfaceTextureHelper surfaceTextureHelper, String cameraId, - int width, int height, int framerate) { + int width, int height, int framerate, boolean torch) { Logging.d(TAG, "Create new camera2 session on camera " + cameraId); constructionTimeNs = System.nanoTime(); @@ -292,6 +298,7 @@ private Camera2Session(CreateSessionCallback callback, Events events, Context ap this.width = width; this.height = height; this.framerate = framerate; + this.torch = torch; start(); } diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java index 1922a529e..9aef5bffc 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java @@ -40,7 +40,8 @@ enum SwitchState { @Override public void onDone(CameraSession session) { checkIsOnCameraThread(); - Logging.d(TAG, "Create session done. Switch state: " + switchState); + Logging.d(TAG, "Create session done. Switch state: " + switchState + + ". Torch state: " + torchState); uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); synchronized (stateLock) { capturerObserver.onCapturerStarted(true /* success */); @@ -62,6 +63,17 @@ public void onDone(CameraSession session) { switchState = SwitchState.IDLE; switchCameraInternal(switchEventsHandler, selectedCameraName); } + + if (CameraCapturer.this.torchState == CameraCapturer.SwitchState.IN_PROGRESS) { + CameraCapturer.this.torchState = CameraCapturer.SwitchState.IDLE; + if (CameraCapturer.this.torchHandler != null) { + CameraCapturer.this.torchHandler.onTorchSuccess(); + CameraCapturer.this.torchHandler = null; + } + } else if (CameraCapturer.this.torchState == CameraCapturer.SwitchState.PENDING) { + CameraCapturer.this.torchState = CameraCapturer.SwitchState.IDLE; + CameraCapturer.this.torchInternal(!CameraCapturer.this.torch, CameraCapturer.this.torchHandler); + } } } @@ -188,6 +200,8 @@ public void run() { @Nullable private CameraSession currentSession; /* guarded by stateLock */ private String cameraName; /* guarded by stateLock */ private String pendingCameraName; /* guarded by stateLock */ + + private boolean torch; private int width; /* guarded by stateLock */ private int height; /* guarded by stateLock */ private int framerate; /* guarded by stateLock */ @@ -195,6 +209,12 @@ public void run() { private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */ @Nullable private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */ // Valid from onDone call until stopCapture, otherwise null. + + private SwitchState torchState; /* guarded by stateLock */ + + @Nullable + private CameraVideoCapturer.TorchHandler torchHandler; /* guarded by stateLock */ + @Nullable private CameraStatistics cameraStatistics; /* guarded by stateLock */ private boolean firstFrameObserved; /* guarded by stateLock */ @@ -218,6 +238,7 @@ public void onCameraClosed() {} } this.eventsHandler = eventsHandler; + this.torchState = SwitchState.IDLE; this.cameraEnumerator = cameraEnumerator; this.cameraName = cameraName; List deviceNames = Arrays.asList(cameraEnumerator.getDeviceNames()); @@ -270,7 +291,7 @@ private void createSessionInternal(int delayMs) { @Override public void run() { createCameraSession(createSessionCallback, cameraSessionEventsHandler, applicationContext, - surfaceHelper, cameraName, width, height, framerate); + surfaceHelper, cameraName, width, height, framerate, torch); } }, delayMs); } @@ -347,6 +368,15 @@ public void run() { }); } + public void torch(final boolean state, final CameraVideoCapturer.TorchHandler torchHandler) { + Logging.d("CameraCapturer", "torch"); + this.cameraThreadHandler.post(new Runnable() { + public void run() { + CameraCapturer.this.torchInternal(state, torchHandler); + } + }); + } + @Override public void switchCamera(final CameraSwitchHandler switchEventsHandler, final String cameraName) { Logging.d(TAG, "switchCamera"); @@ -387,6 +417,13 @@ private void reportCameraSwitchError( } } + private void reportTorchError(String error, @Nullable CameraVideoCapturer.TorchHandler torchHandler) { + Logging.e("CameraCapturer", error); + if (torchHandler != null) { + torchHandler.onTorchError(error); + } + } + private void switchCameraInternal( @Nullable final CameraSwitchHandler switchEventsHandler, final String selectedCameraName) { Logging.d(TAG, "switchCamera internal"); @@ -403,6 +440,10 @@ private void switchCameraInternal( reportCameraSwitchError("Camera switch already in progress.", switchEventsHandler); return; } + if (this.torchState != CameraCapturer.SwitchState.IDLE) { + this.reportCameraSwitchError("Torch change in progress.", switchEventsHandler); + return; + } if (!sessionOpening && currentSession == null) { reportCameraSwitchError("switchCamera: camera is not running.", switchEventsHandler); return; @@ -438,6 +479,57 @@ public void run() { Logging.d(TAG, "switchCamera done"); } + private void torchInternal(boolean state, CameraVideoCapturer.TorchHandler torchHandler) { + Logging.d("CameraCapturer", "torch internal"); + if (!this.cameraEnumerator.hasTorch(this.cameraName)) { + if (torchHandler != null) { + torchHandler.onTorchUnsupported(); + } + + } else { + synchronized(this.stateLock) { + if (this.switchState != CameraCapturer.SwitchState.IDLE) { + this.reportTorchError("Camera switch in progress.", torchHandler); + return; + } + + if (this.torchState != CameraCapturer.SwitchState.IDLE) { + this.reportTorchError("Torch change already in progress.", torchHandler); + return; + } + + if (!this.sessionOpening && this.currentSession == null) { + this.reportTorchError("torch: camera is not running.", torchHandler); + return; + } + + this.torchHandler = torchHandler; + if (this.sessionOpening) { + this.torchState = CameraCapturer.SwitchState.PENDING; + return; + } + + this.torchState = CameraCapturer.SwitchState.IN_PROGRESS; + Logging.d("CameraCapturer", "torch: Stopping session"); + this.cameraStatistics.release(); + this.cameraStatistics = null; + final CameraSession oldSession = this.currentSession; + this.cameraThreadHandler.post(new Runnable() { + public void run() { + oldSession.stop(); + } + }); + this.currentSession = null; + this.torch = state; + this.sessionOpening = true; + this.openAttemptsRemaining = 1; + this.createSessionInternal(0); + } + + Logging.d("CameraCapturer", "torch done"); + } + } + private void checkIsOnCameraThread() { if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { Logging.e(TAG, "Check is on camera thread failed."); @@ -454,5 +546,5 @@ protected String getCameraName() { abstract protected void createCameraSession( CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events, Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName, - int width, int height, int framerate); + int width, int height, int framerate, boolean torch); } diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraEnumerator.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraEnumerator.java index dc954b62e..632443df7 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraEnumerator.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraEnumerator.java @@ -18,6 +18,7 @@ public interface CameraEnumerator { public String[] getDeviceNames(); public boolean isFrontFacing(String deviceName); public boolean isBackFacing(String deviceName); + public boolean hasTorch(String deviceName); public List getSupportedFormats(String deviceName); public CameraVideoCapturer createCapturer( diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java index ec26868b5..db0095310 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java @@ -43,6 +43,14 @@ public interface CameraEventsHandler { void onCameraClosed(); } + public interface TorchHandler { + void onTorchSuccess(); + + void onTorchError(String var1); + + void onTorchUnsupported(); + } + /** * Camera switch handler - one of these functions are invoked with the result of switchCamera(). * The callback may be called on an arbitrary thread. From 1beeaa4f7de907ab2d1fb1149e5ad4be434de313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20=C5=A0krob=C3=A1k?= Date: Wed, 29 May 2024 10:04:33 +0200 Subject: [PATCH 2/3] Fix torch --- .../src/main/java/org/webrtc/CameraCapturer.java | 1 + .../src/main/java/org/webrtc/CameraVideoCapturer.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java index 9aef5bffc..b1ad3f667 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java @@ -368,6 +368,7 @@ public void run() { }); } + @Override public void torch(final boolean state, final CameraVideoCapturer.TorchHandler torchHandler) { Logging.d("CameraCapturer", "torch"); this.cameraThreadHandler.post(new Runnable() { diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java index db0095310..137f8d953 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java @@ -75,6 +75,8 @@ public interface CameraSwitchHandler { */ void switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName); + public void torch(final boolean state, final CameraVideoCapturer.TorchHandler torchHandler); + /** * MediaRecorder add/remove handler - one of these functions are invoked with the result of * addMediaRecorderToCamera() or removeMediaRecorderFromCamera calls. From c242b1713fa5072b533e0ecb234072306887b30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20=C5=A0krob=C3=A1k?= Date: Wed, 29 May 2024 10:37:12 +0200 Subject: [PATCH 3/3] Comments --- .../src/main/java/org/webrtc/CameraCapturer.java | 2 +- .../src/main/java/org/webrtc/CameraVideoCapturer.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java index b1ad3f667..54ce90bb0 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraCapturer.java @@ -201,7 +201,7 @@ public void run() { private String cameraName; /* guarded by stateLock */ private String pendingCameraName; /* guarded by stateLock */ - private boolean torch; + private boolean torch; /* guarded by stateLock */ private int width; /* guarded by stateLock */ private int height; /* guarded by stateLock */ private int framerate; /* guarded by stateLock */ diff --git a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java index 137f8d953..aebf774c1 100644 --- a/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java +++ b/stream-webrtc-android/src/main/java/org/webrtc/CameraVideoCapturer.java @@ -43,6 +43,10 @@ public interface CameraEventsHandler { void onCameraClosed(); } + /** + * Camera torch handler - one of these functions are invoked with the result of torch(). + * The callback may be called on an arbitrary thread. + */ public interface TorchHandler { void onTorchSuccess(); @@ -75,6 +79,9 @@ public interface CameraSwitchHandler { */ void switchCamera(CameraSwitchHandler switchEventsHandler, String cameraName); + /** + * Switch torch on or off + */ public void torch(final boolean state, final CameraVideoCapturer.TorchHandler torchHandler); /**