Index: webrtc/api/android/java/src/org/webrtc/CameraCapturer.java |
diff --git a/webrtc/api/android/java/src/org/webrtc/CameraCapturer.java b/webrtc/api/android/java/src/org/webrtc/CameraCapturer.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..75712cc57031f255b4e19f92c021d83ab1bb0c34 |
--- /dev/null |
+++ b/webrtc/api/android/java/src/org/webrtc/CameraCapturer.java |
@@ -0,0 +1,285 @@ |
+/* |
+ * Copyright 2016 The WebRTC project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+package org.webrtc; |
+ |
+import org.webrtc.CameraEnumerationAndroid.CaptureFormat; |
+ |
+import android.content.Context; |
+import android.os.Handler; |
+import android.os.SystemClock; |
+ |
+import java.util.Arrays; |
+import java.util.List; |
+ |
+@SuppressWarnings("deprecation") |
+public abstract class CameraCapturer implements CameraVideoCapturer { |
+ private static final String TAG = "CameraCapturer"; |
+ private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; |
+ private final static int OPEN_CAMERA_DELAY_MS = 500; |
+ |
+ private final CameraEnumerator cameraEnumerator; |
+ private final CameraEventsHandler eventsHandler; |
+ |
+ private final CameraSession.CreateSessionCallback createSessionCallback = |
+ new CameraSession.CreateSessionCallback() { |
+ @Override |
+ public void onDone(CameraSession session) { |
+ Logging.d(TAG, "Create session done"); |
+ |
+ synchronized (stateLock) { |
+ sessionOpening = false; |
+ currentSession = session; |
+ stateLock.notifyAll(); |
+ |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchDone( |
+ cameraEnumerator.isFrontFacing(cameraName)); |
+ switchEventsHandler = null; |
+ } |
+ switchInProgress = false; |
+ } |
+ } |
+ |
+ @Override |
+ public void onFailure(String error) { |
+ synchronized (stateLock) { |
+ openAttemptsRemaining--; |
+ |
+ if (openAttemptsRemaining <= 0) { |
+ Logging.w(TAG, "Opening camera failed, passing: " + error); |
+ sessionOpening = false; |
+ stateLock.notifyAll(); |
+ |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchError(error); |
+ switchEventsHandler = null; |
+ } |
+ switchInProgress = false; |
+ |
+ eventsHandler.onCameraError(error); |
+ } else { |
+ Logging.w(TAG, "Opening camera failed, retry: " + error); |
+ |
+ createSessionInternal(OPEN_CAMERA_DELAY_MS); |
+ } |
+ } |
+ } |
+ }; |
+ |
+ // Initialized on initialize |
+ // ------------------------- |
+ // Use postOnCameraThread() instead of posting directly to the handler - this way all |
+ // callbacks with a specifed token can be removed at once. |
+ private Handler cameraThreadHandler; |
+ private Context applicationContext; |
+ private CapturerObserver capturerObserver; |
+ private SurfaceTextureHelper surfaceHelper; |
+ |
+ private final Object stateLock = new Object(); |
+ private boolean sessionOpening; /* guarded by stateLock */ |
+ private CameraSession currentSession; /* guarded by stateLock */ |
+ private String cameraName; /* guarded by stateLock */ |
+ private int width; /* guarded by stateLock */ |
+ private int height; /* guarded by stateLock */ |
+ private int framerate; /* guarded by stateLock */ |
+ private int openAttemptsRemaining; /* guarded by stateLock */ |
+ private boolean switchInProgress; /* guarded by stateLock */ |
+ private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */ |
+ |
+ public CameraCapturer( |
+ String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cameraEnumerator) { |
+ if (eventsHandler == null) { |
+ eventsHandler = new CameraEventsHandler() { |
+ @Override |
+ public void onCameraError(String errorDescription) {} |
+ @Override |
+ public void onCameraFreezed(String errorDescription) {} |
+ @Override |
+ public void onCameraOpening(int cameraId) {} |
+ @Override |
+ public void onFirstFrameAvailable() {} |
+ @Override |
+ public void onCameraClosed() {} |
+ }; |
+ } |
+ |
+ this.eventsHandler = eventsHandler; |
+ this.cameraEnumerator = cameraEnumerator; |
+ this.cameraName = cameraName; |
+ |
+ final String[] deviceNames = cameraEnumerator.getDeviceNames(); |
+ |
+ if (deviceNames.length == 0) { |
+ throw new RuntimeException("No cameras attached."); |
+ } |
+ if (!Arrays.asList(deviceNames).contains(this.cameraName)) { |
+ throw new IllegalArgumentException( |
+ "Camera name " + this.cameraName + " does not match any known camera device."); |
+ } |
+ } |
+ |
+ @Override |
+ public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext, |
+ CapturerObserver capturerObserver) { |
+ this.applicationContext = applicationContext; |
+ this.capturerObserver = capturerObserver; |
+ this.surfaceHelper = surfaceTextureHelper; |
+ this.cameraThreadHandler = |
+ surfaceTextureHelper == null ? null : surfaceTextureHelper.getHandler(); |
+ } |
+ |
+ @Override |
+ public void startCapture(int width, int height, int framerate) { |
+ Logging.d(TAG, "startCapture: " + width + "x" + height + "@" + framerate); |
+ |
+ synchronized (stateLock) { |
+ if (sessionOpening || currentSession != null) { |
+ Logging.w(TAG, "Session already open"); |
+ return; |
+ } |
+ |
+ this.width = width; |
+ this.height = height; |
+ this.framerate = framerate; |
+ |
+ sessionOpening = true; |
+ openAttemptsRemaining = MAX_OPEN_CAMERA_ATTEMPTS; |
+ createSessionInternal(0); |
+ } |
+ } |
+ |
+ private void createSessionInternal(int delayMs) { |
+ cameraThreadHandler.postDelayed(new Runnable() { |
+ @Override |
+ public void run() { |
+ createCameraSession( |
+ createSessionCallback, |
+ eventsHandler, applicationContext, capturerObserver, surfaceHelper, |
+ cameraName, width, height, framerate); |
+ } |
+ }, delayMs); |
+ } |
+ |
+ @Override |
+ public void stopCapture() { |
+ Logging.d(TAG, "Stop capture"); |
+ |
+ synchronized (stateLock) { |
+ while (sessionOpening) { |
+ Logging.d(TAG, "Stop capture: Waiting for session to open"); |
+ ThreadUtils.waitUninterruptibly(stateLock); |
+ } |
+ |
+ if (currentSession != null) { |
+ Logging.d(TAG, "Stop capture: Stopping session"); |
+ currentSession.stop(); |
+ currentSession = null; |
+ } else { |
+ Logging.d(TAG, "Stop capture: No session open"); |
+ } |
+ } |
+ |
+ Logging.d(TAG, "Stop capture done"); |
+ } |
+ |
+ @Override |
+ public void onOutputFormatRequest(final int width, final int height, final int framerate) { |
+ cameraThreadHandler.post(new Runnable() { |
+ @Override public void run() { |
+ Logging.d(TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + height + |
+ "@" + framerate); |
+ capturerObserver.onOutputFormatRequest(width, height, framerate); |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ public void changeCaptureFormat(int width, int height, int framerate) { |
+ Logging.d(TAG, "changeCaptureFormat: " + width + "x" + height + "@" + framerate); |
+ synchronized (stateLock) { |
+ stopCapture(); |
+ startCapture(width, height, framerate); |
+ } |
+ } |
+ |
+ @Override |
+ public void dispose() { |
+ Logging.d(TAG, "dispose"); |
+ stopCapture(); |
+ } |
+ |
+ @Override |
+ public void switchCamera(final CameraSwitchHandler switchEventsHandler) { |
+ Logging.d(TAG, "switchCamera"); |
+ |
+ final String[] deviceNames = cameraEnumerator.getDeviceNames(); |
+ |
+ if (deviceNames.length < 2) { |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchError("No camera to switch to."); |
+ } |
+ return; |
+ } |
+ |
+ synchronized (stateLock) { |
+ if (switchInProgress) { |
+ Logging.d(TAG, "switchCamera switchInProgress"); |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchError("Camera switch already in progress."); |
+ } |
+ return; |
+ } |
+ |
+ if (sessionOpening) { |
+ Logging.d(TAG, "switchCamera sessionOpening"); |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchError("Session is still opening."); |
+ } |
+ return; |
+ } |
+ |
+ if (currentSession == null) { |
+ Logging.d(TAG, "switchCamera: No session open"); |
+ if (switchEventsHandler != null) { |
+ switchEventsHandler.onCameraSwitchError("Camera is not running."); |
+ } |
+ return; |
+ } |
+ |
+ Logging.d(TAG, "switchCamera: Stopping session"); |
+ currentSession.stop(); |
+ currentSession = null; |
+ |
+ int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName); |
+ cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length]; |
+ |
+ switchInProgress = true; |
+ this.switchEventsHandler = switchEventsHandler; |
+ sessionOpening = true; |
+ openAttemptsRemaining = 1; |
+ createSessionInternal(0); |
+ } |
+ Logging.d(TAG, "switchCamera done"); |
+ } |
+ |
+ protected String getCameraName() { |
+ synchronized (stateLock) { |
+ return cameraName; |
+ } |
+ } |
+ |
+ abstract protected void createCameraSession( |
+ CameraSession.CreateSessionCallback createSessionCallback, |
+ CameraEventsHandler eventsHandler, Context applicationContext, |
+ CameraVideoCapturer.CapturerObserver capturerObserver, |
+ SurfaceTextureHelper surfaceTextureHelper, |
+ String cameraName, int width, int height, int framerate); |
+} |