Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(451)

Unified Diff: webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java

Issue 2186253002: Session based capturing for Camera2Capturer. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Rebase Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/api/BUILD.gn ('k') | webrtc/api/android/java/src/org/webrtc/Camera2Session.java » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
diff --git a/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java b/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
index d26275cb0d3a3728dd61632494079fbd23989b86..f1c37a155bb530a9afbfbf231d26a72324c91242 100644
--- a/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
+++ b/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
@@ -14,879 +14,41 @@ import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import android.annotation.TargetApi;
import android.content.Context;
-import android.graphics.SurfaceTexture;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.TotalCaptureResult;
import android.os.Handler;
-import android.os.SystemClock;
-import android.util.Range;
-import android.view.Surface;
-import android.view.WindowManager;
-import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
@TargetApi(21)
-public class Camera2Capturer implements
- CameraVideoCapturer,
- SurfaceTextureHelper.OnTextureFrameAvailableListener {
- private final static String TAG = "Camera2Capturer";
-
- private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
- private final static int OPEN_CAMERA_DELAY_MS = 500;
- private final static int STOP_TIMEOUT = 10000;
- private final static int START_TIMEOUT = 10000;
- private final static Object STOP_TIMEOUT_RUNNABLE_TOKEN = new Object();
-
- // In the Camera2 API, starting a camera is inherently asynchronous, and this state is
- // represented with 'STARTING'. Stopping is also asynchronous and this state is 'STOPPING'.
- private static enum CameraState { IDLE, STARTING, RUNNING, STOPPING }
-
- // Thread safe objects.
- // --------------------
+public class Camera2Capturer extends CameraCapturer {
+ private final Context context;
private final CameraManager cameraManager;
- private final CameraEventsHandler eventsHandler;
-
- // Set once in initialization(), before any other calls, so therefore thread safe.
- // ---------------------------------------------------------------------------------------------
- private SurfaceTextureHelper surfaceTextureHelper;
- private Context applicationContext;
- private CapturerObserver capturerObserver;
- // 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;
-
- // Shared state - guarded by cameraStateLock. Will only be edited from camera thread (when it is
- // running).
- // ---------------------------------------------------------------------------------------------
- private final Object cameraStateLock = new Object();
- private volatile CameraState cameraState = CameraState.IDLE;
- // Remember the requested format in case we want to switch cameras.
- private int requestedWidth;
- private int requestedHeight;
- private int requestedFramerate;
-
- // Will only be edited while camera state is IDLE and cameraStateLock is acquired.
- private String cameraName;
- private boolean isFrontCamera;
- private int cameraOrientation;
-
- // Atomic boolean for allowing only one switch at a time.
- private final AtomicBoolean isPendingCameraSwitch = new AtomicBoolean();
- // Guarded by isPendingCameraSwitch.
- private CameraSwitchHandler switchEventsHandler;
-
- // Internal state - must only be modified from camera thread
- // ---------------------------------------------------------
- private CaptureFormat captureFormat;
- private CameraStatistics cameraStatistics;
- private CameraCaptureSession captureSession;
- private Surface surface;
- private CameraDevice cameraDevice;
-
- // Factor to convert between Android framerates and CaptureFormat.FramerateRange. It will be
- // either 1 or 1000.
- private int fpsUnitFactor;
- private boolean firstFrameReported;
- private int consecutiveCameraOpenFailures;
-
- public Camera2Capturer(
- Context context, String cameraName, CameraEventsHandler eventsHandler) {
- Logging.d(TAG, "Camera2Capturer ctor, camera name: " + cameraName);
- this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- this.eventsHandler = eventsHandler;
-
- setCameraName(cameraName);
- }
-
- private boolean isOnCameraThread() {
- return Thread.currentThread() == cameraThreadHandler.getLooper().getThread();
- }
-
- /**
- * Helper method for checking method is executed on camera thread.
- */
- private void checkIsOnCameraThread() {
- if (!isOnCameraThread()) {
- throw new IllegalStateException("Not on camera thread");
- }
- }
-
- /**
- * Checks method is not invoked on the camera thread. Used in functions waiting for the camera
- * state to change since executing them on the camera thread would cause a deadlock.
- */
- private void checkNotOnCameraThread() {
- if (cameraThreadHandler == null) {
- return;
- }
-
- if (Thread.currentThread() == cameraThreadHandler.getLooper().getThread()) {
- throw new IllegalStateException(
- "Method waiting for camera state to change executed on camera thread");
- }
- }
-
- private void waitForCameraToExitTransitionalState(
- CameraState transitionalState, long timeoutMs) {
- checkNotOnCameraThread();
-
- // We probably should already have the lock when this is called but acquire it in case
- // we don't have it.
- synchronized (cameraStateLock) {
- long timeoutAt = SystemClock.uptimeMillis() + timeoutMs;
-
- while (cameraState == transitionalState) {
- Logging.d(TAG, "waitForCameraToExitTransitionalState waiting: "
- + cameraState);
-
- long timeLeft = timeoutAt - SystemClock.uptimeMillis();
-
- if (timeLeft <= 0) {
- Logging.e(TAG, "Camera failed to exit transitional state " + transitionalState
- + " within the time limit.");
- break;
- }
-
- try {
- cameraStateLock.wait(timeLeft);
- } catch (InterruptedException e) {
- Logging.w(TAG, "Trying to interrupt while waiting to exit transitional state "
- + transitionalState + ", ignoring: " + e);
- }
- }
- }
- }
-
- /**
- * Waits until camera state is not STOPPING.
- */
- private void waitForCameraToStopIfStopping() {
- waitForCameraToExitTransitionalState(CameraState.STOPPING, STOP_TIMEOUT);
- }
-
- /**
- * Wait until camera state is not STARTING.
- */
- private void waitForCameraToStartIfStarting() {
- waitForCameraToExitTransitionalState(CameraState.STARTING, START_TIMEOUT);
- }
-
- /**
- * Sets the name of the camera. Camera must be stopped or stopping when this is called.
- */
- private void setCameraName(String cameraName) {
- final CameraCharacteristics characteristics;
- try {
- final String[] cameraIds = cameraManager.getCameraIdList();
-
- if (cameraName.isEmpty() && cameraIds.length != 0) {
- cameraName = cameraIds[0];
- }
-
- if (!Arrays.asList(cameraIds).contains(cameraName)) {
- throw new IllegalArgumentException(
- "Camera name: " + cameraName + " does not match any known camera device:");
- }
-
- characteristics = cameraManager.getCameraCharacteristics(cameraName);
- } catch (CameraAccessException e) {
- throw new RuntimeException("Camera access exception: " + e);
- }
-
- synchronized (cameraStateLock) {
- waitForCameraToStopIfStopping();
-
- if (cameraState != CameraState.IDLE) {
- throw new RuntimeException("Changing camera name on running camera.");
- }
-
- // Note: Usually changing camera state from outside camera thread is not allowed. It is
- // allowed here because camera is not running.
- this.cameraName = cameraName;
- isFrontCamera = characteristics.get(CameraCharacteristics.LENS_FACING)
- == CameraMetadata.LENS_FACING_FRONT;
-
- /*
- * Clockwise angle through which the output image needs to be rotated to be upright on the
- * device screen in its native orientation.
- * Also defines the direction of rolling shutter readout, which is from top to bottom in the
- * sensor's coordinate system.
- * Units: Degrees of clockwise rotation; always a multiple of 90
- */
- cameraOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- }
- }
-
- /**
- * Triggers appropriate error handlers based on the camera state. Must be called on the camera
- * thread and camera must not be stopped.
- */
- private void reportError(String errorDescription) {
- checkIsOnCameraThread();
- Logging.e(TAG, "Error in camera at state " + cameraState + ": " + errorDescription);
-
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchError(errorDescription);
- switchEventsHandler = null;
- }
- isPendingCameraSwitch.set(false);
-
- switch (cameraState) {
- case STARTING:
- capturerObserver.onCapturerStarted(false /* success */);
- // fall through
- case RUNNING:
- if (eventsHandler != null) {
- eventsHandler.onCameraError(errorDescription);
- }
- break;
- case STOPPING:
- setCameraState(CameraState.IDLE);
- Logging.e(TAG, "Closing camera failed: " + errorDescription);
- return; // We don't want to call closeAndRelease in this case.
- default:
- throw new RuntimeException("Unknown camera state: " + cameraState);
- }
- closeAndRelease();
- }
-
- private void closeAndRelease() {
- checkIsOnCameraThread();
-
- Logging.d(TAG, "Close and release.");
- setCameraState(CameraState.STOPPING);
- capturerObserver.onCapturerStopped();
-
- // Remove all pending Runnables posted from |this|.
- cameraThreadHandler.removeCallbacksAndMessages(this /* token */);
- if (cameraStatistics != null) {
- cameraStatistics.release();
- cameraStatistics = null;
- }
- if (surfaceTextureHelper != null) {
- surfaceTextureHelper.stopListening();
- }
- if (captureSession != null) {
- captureSession.close();
- captureSession = null;
- }
- if (surface != null) {
- surface.release();
- surface = null;
- }
- if (cameraDevice != null) {
- // Add a timeout for stopping the camera.
- cameraThreadHandler.postAtTime(new Runnable() {
- @Override
- public void run() {
- Logging.e(TAG, "Camera failed to stop within the timeout. Force stopping.");
- setCameraState(CameraState.IDLE);
- if (eventsHandler != null) {
- eventsHandler.onCameraError("Camera failed to stop (timeout).");
- }
- }
- }, STOP_TIMEOUT_RUNNABLE_TOKEN, SystemClock.uptimeMillis() + STOP_TIMEOUT);
-
- cameraDevice.close();
- cameraDevice = null;
- } else {
- Logging.w(TAG, "closeAndRelease called while cameraDevice is null");
- setCameraState(CameraState.IDLE);
- }
- }
-
- /**
- * Sets the camera state while ensuring constraints are followed.
- */
- private void setCameraState(CameraState newState) {
- // State must only be modified on the camera thread. It can be edited from other threads
- // if cameraState is IDLE since the camera thread is idle and not modifying the state.
- if (cameraState != CameraState.IDLE) {
- checkIsOnCameraThread();
- }
-
- switch (newState) {
- case STARTING:
- if (cameraState != CameraState.IDLE) {
- throw new IllegalStateException("Only stopped camera can start.");
- }
- break;
- case RUNNING:
- if (cameraState != CameraState.STARTING) {
- throw new IllegalStateException("Only starting camera can go to running state.");
- }
- break;
- case STOPPING:
- if (cameraState != CameraState.STARTING && cameraState != CameraState.RUNNING) {
- throw new IllegalStateException("Only starting or running camera can stop.");
- }
- break;
- case IDLE:
- if (cameraState != CameraState.STOPPING) {
- throw new IllegalStateException("Only stopping camera can go to idle state.");
- }
- break;
- default:
- throw new RuntimeException("Unknown camera state: " + newState);
- }
-
- synchronized (cameraStateLock) {
- cameraState = newState;
- cameraStateLock.notifyAll();
- }
- }
-
- /**
- * Internal method for opening the camera. Must be called on the camera thread.
- */
- private void openCamera() {
- try {
- checkIsOnCameraThread();
-
- if (cameraState != CameraState.STARTING) {
- throw new IllegalStateException("Camera should be in state STARTING in openCamera.");
- }
-
- // Camera is in state STARTING so cameraName will not be edited.
- cameraManager.openCamera(cameraName, new CameraStateCallback(), cameraThreadHandler);
- } catch (CameraAccessException e) {
- reportError("Failed to open camera: " + e);
- }
- }
-
- private boolean isInitialized() {
- return applicationContext != null && capturerObserver != null;
- }
-
- @Override
- public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext,
- CapturerObserver capturerObserver) {
- Logging.d(TAG, "initialize");
- if (applicationContext == null) {
- throw new IllegalArgumentException("applicationContext not set.");
- }
- if (capturerObserver == null) {
- throw new IllegalArgumentException("capturerObserver not set.");
- }
- if (isInitialized()) {
- throw new IllegalStateException("Already initialized");
- }
- this.applicationContext = applicationContext;
- this.capturerObserver = capturerObserver;
- this.surfaceTextureHelper = surfaceTextureHelper;
- this.cameraThreadHandler =
- surfaceTextureHelper == null ? null : surfaceTextureHelper.getHandler();
- }
-
- private void startCaptureOnCameraThread(
- final int requestedWidth, final int requestedHeight, final int requestedFramerate) {
- checkIsOnCameraThread();
-
- firstFrameReported = false;
- consecutiveCameraOpenFailures = 0;
-
- synchronized (cameraStateLock) {
- // Remember the requested format in case we want to switch cameras.
- this.requestedWidth = requestedWidth;
- this.requestedHeight = requestedHeight;
- this.requestedFramerate = requestedFramerate;
- }
-
- final CameraCharacteristics cameraCharacteristics;
- try {
- // Camera is in state STARTING so cameraName will not be edited.
- cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraName);
- } catch (CameraAccessException e) {
- reportError("getCameraCharacteristics(): " + e.getMessage());
- return;
- }
-
- Range<Integer>[] fpsRanges =
- cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
- fpsUnitFactor = Camera2Enumerator.getFpsUnitFactor(fpsRanges);
- List<CaptureFormat.FramerateRange> framerateRanges =
- Camera2Enumerator.convertFramerates(fpsRanges, fpsUnitFactor);
- List<Size> sizes = Camera2Enumerator.getSupportedSizes(cameraCharacteristics);
-
- if (framerateRanges.isEmpty() || sizes.isEmpty()) {
- reportError("No supported capture formats.");
- }
- final CaptureFormat.FramerateRange bestFpsRange =
- CameraEnumerationAndroid.getClosestSupportedFramerateRange(
- framerateRanges, requestedFramerate);
-
- final Size bestSize = CameraEnumerationAndroid.getClosestSupportedSize(
- sizes, requestedWidth, requestedHeight);
-
- this.captureFormat = new CaptureFormat(bestSize.width, bestSize.height, bestFpsRange);
- Logging.d(TAG, "Using capture format: " + captureFormat);
-
- Logging.d(TAG, "Opening camera " + cameraName);
- if (eventsHandler != null) {
- int cameraIndex = -1;
- try {
- cameraIndex = Integer.parseInt(cameraName);
- } catch (NumberFormatException e) {
- Logging.d(TAG, "External camera with non-int identifier: " + cameraName);
- }
- eventsHandler.onCameraOpening(cameraIndex);
- }
-
- openCamera();
- }
-
- /**
- * Starts capture using specified settings. This is automatically called for you by
- * VideoCapturerTrackSource if you are just using the camera as source for video track.
- */
- @Override
- public void startCapture(
- final int requestedWidth, final int requestedHeight, final int requestedFramerate) {
- Logging.d(TAG, "startCapture requested: " + requestedWidth + "x" + requestedHeight
- + "@" + requestedFramerate);
- if (!isInitialized()) {
- throw new IllegalStateException("startCapture called in uninitialized state");
- }
- if (surfaceTextureHelper == null) {
- capturerObserver.onCapturerStarted(false /* success */);
- if (eventsHandler != null) {
- eventsHandler.onCameraError("No SurfaceTexture created.");
- }
- return;
- }
- synchronized (cameraStateLock) {
- waitForCameraToStopIfStopping();
- if (cameraState != CameraState.IDLE) {
- Logging.e(TAG, "Unexpected camera state for startCapture: " + cameraState);
- return;
- }
- setCameraState(CameraState.STARTING);
- }
- postOnCameraThread(new Runnable() {
- @Override
- public void run() {
- startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate);
- }
- });
- }
-
- final class CameraStateCallback extends CameraDevice.StateCallback {
- private String getErrorDescription(int errorCode) {
- switch (errorCode) {
- case CameraDevice.StateCallback.ERROR_CAMERA_DEVICE:
- return "Camera device has encountered a fatal error.";
- case CameraDevice.StateCallback.ERROR_CAMERA_DISABLED:
- return "Camera device could not be opened due to a device policy.";
- case CameraDevice.StateCallback.ERROR_CAMERA_IN_USE:
- return "Camera device is in use already.";
- case CameraDevice.StateCallback.ERROR_CAMERA_SERVICE:
- return "Camera service has encountered a fatal error.";
- case CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE:
- return "Camera device could not be opened because"
- + " there are too many other open camera devices.";
- default:
- return "Unknown camera error: " + errorCode;
- }
- }
-
- @Override
- public void onDisconnected(CameraDevice camera) {
- checkIsOnCameraThread();
- cameraDevice = camera;
- reportError("Camera disconnected.");
- }
-
- @Override
- public void onError(CameraDevice camera, int errorCode) {
- checkIsOnCameraThread();
- cameraDevice = camera;
-
- if (cameraState == CameraState.STARTING && (
- errorCode == CameraDevice.StateCallback.ERROR_CAMERA_IN_USE ||
- errorCode == CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE)) {
- consecutiveCameraOpenFailures++;
-
- if (consecutiveCameraOpenFailures < MAX_OPEN_CAMERA_ATTEMPTS) {
- Logging.w(TAG, "Opening camera failed, trying again: " + getErrorDescription(errorCode));
-
- postDelayedOnCameraThread(OPEN_CAMERA_DELAY_MS, new Runnable() {
- @Override
- public void run() {
- openCamera();
- }
- });
- return;
- } else {
- Logging.e(TAG, "Opening camera failed too many times. Passing the error.");
- }
- }
-
- reportError(getErrorDescription(errorCode));
- }
-
- @Override
- public void onOpened(CameraDevice camera) {
- checkIsOnCameraThread();
-
- Logging.d(TAG, "Camera opened.");
- if (cameraState != CameraState.STARTING) {
- throw new IllegalStateException("Unexpected state when camera opened: " + cameraState);
- }
-
- cameraDevice = camera;
- final SurfaceTexture surfaceTexture = surfaceTextureHelper.getSurfaceTexture();
- surfaceTexture.setDefaultBufferSize(captureFormat.width, captureFormat.height);
- surface = new Surface(surfaceTexture);
- try {
- camera.createCaptureSession(
- Arrays.asList(surface), new CaptureSessionCallback(), cameraThreadHandler);
- } catch (CameraAccessException e) {
- reportError("Failed to create capture session. " + e);
- }
- }
-
- @Override
- public void onClosed(CameraDevice camera) {
- checkIsOnCameraThread();
-
- Logging.d(TAG, "Camera device closed.");
-
- if (cameraState != CameraState.STOPPING) {
- Logging.e(TAG, "Camera state was not STOPPING in onClosed. Most likely camera didn't stop "
- + "within timelimit and this method was invoked twice.");
- return;
- }
-
- cameraThreadHandler.removeCallbacksAndMessages(STOP_TIMEOUT_RUNNABLE_TOKEN);
- setCameraState(CameraState.IDLE);
- if (eventsHandler != null) {
- eventsHandler.onCameraClosed();
- }
- }
- }
-
- final class CaptureSessionCallback extends CameraCaptureSession.StateCallback {
- @Override
- public void onConfigureFailed(CameraCaptureSession session) {
- checkIsOnCameraThread();
- captureSession = session;
- reportError("Failed to configure capture session.");
- }
-
- @Override
- public void onConfigured(CameraCaptureSession session) {
- checkIsOnCameraThread();
- Logging.d(TAG, "Camera capture session configured.");
- captureSession = session;
- try {
- /*
- * The viable options for video capture requests are:
- * TEMPLATE_PREVIEW: High frame rate is given priority over the highest-quality
- * post-processing.
- * TEMPLATE_RECORD: Stable frame rate is used, and post-processing is set for recording
- * quality.
- */
- final CaptureRequest.Builder captureRequestBuilder =
- cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
- // Set auto exposure fps range.
- captureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<Integer>(
- captureFormat.framerate.min / fpsUnitFactor,
- captureFormat.framerate.max / fpsUnitFactor));
- captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON);
- captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
-
- captureRequestBuilder.addTarget(surface);
- session.setRepeatingRequest(
- captureRequestBuilder.build(), new CameraCaptureCallback(), cameraThreadHandler);
- } catch (CameraAccessException e) {
- reportError("Failed to start capture request. " + e);
- return;
- }
-
- Logging.d(TAG, "Camera device successfully started.");
- surfaceTextureHelper.startListening(Camera2Capturer.this);
- capturerObserver.onCapturerStarted(true /* success */);
- cameraStatistics = new CameraStatistics(surfaceTextureHelper, eventsHandler);
- setCameraState(CameraState.RUNNING);
-
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchDone(isFrontCamera);
- switchEventsHandler = null;
- }
- isPendingCameraSwitch.set(false);
- }
- }
-
- final class CameraCaptureCallback extends CameraCaptureSession.CaptureCallback {
- static final int MAX_CONSECUTIVE_CAMERA_CAPTURE_FAILURES = 10;
- int consecutiveCameraCaptureFailures;
-
- @Override
- public void onCaptureFailed(
- CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
- checkIsOnCameraThread();
- ++consecutiveCameraCaptureFailures;
- if (consecutiveCameraCaptureFailures > MAX_CONSECUTIVE_CAMERA_CAPTURE_FAILURES) {
- reportError("Capture failed " + consecutiveCameraCaptureFailures + " consecutive times.");
- }
- }
-
- @Override
- public void onCaptureCompleted(
- CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
- // TODO(sakal): This sometimes gets called after camera has stopped, investigate
- checkIsOnCameraThread();
- consecutiveCameraCaptureFailures = 0;
- }
- }
-
-
-
- // Switch camera to the next valid camera id. This can only be called while
- // the camera is running.
- @Override
- public void switchCamera(final CameraSwitchHandler switchEventsHandler) {
- final String[] cameraIds;
- try {
- cameraIds = cameraManager.getCameraIdList();
- } catch (CameraAccessException e) {
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchError("Could not get camera names: " + e);
- }
- return;
- }
- if (cameraIds.length < 2) {
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchError("No camera to switch to.");
- }
- return;
- }
- // Do not handle multiple camera switch request to avoid blocking camera thread by handling too
- // many switch request from a queue. We have to be careful to always release
- // |isPendingCameraSwitch| by setting it to false when done.
- if (isPendingCameraSwitch.getAndSet(true)) {
- Logging.w(TAG, "Ignoring camera switch request.");
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchError("Pending camera switch already in progress.");
- }
- return;
- }
-
- final String newCameraId;
- final int requestedWidth;
- final int requestedHeight;
- final int requestedFramerate;
-
- synchronized (cameraStateLock) {
- waitForCameraToStartIfStarting();
-
- if (cameraState != CameraState.RUNNING) {
- Logging.e(TAG, "Calling swithCamera() on stopped camera.");
- if (switchEventsHandler != null) {
- switchEventsHandler.onCameraSwitchError("Camera is stopped.");
- }
- isPendingCameraSwitch.set(false);
- return;
- }
-
- // Calculate new camera index and camera id. Camera is in state RUNNING so cameraName will
- // not be edited.
- final int currentCameraIndex = Arrays.asList(cameraIds).indexOf(cameraName);
- if (currentCameraIndex == -1) {
- Logging.e(TAG, "Couldn't find current camera id " + cameraName
- + " in list of camera ids: " + Arrays.toString(cameraIds));
- }
- final int newCameraIndex = (currentCameraIndex + 1) % cameraIds.length;
- newCameraId = cameraIds[newCameraIndex];
-
- requestedWidth = this.requestedWidth;
- requestedHeight = this.requestedHeight;
- requestedFramerate = this.requestedFramerate;
- this.switchEventsHandler = switchEventsHandler;
- }
-
- // Make the switch.
- stopCapture();
- setCameraName(newCameraId);
- startCapture(requestedWidth, requestedHeight, requestedFramerate);
-
- // Note: switchEventsHandler will be called from onConfigured / reportError.
- }
-
- // Requests a new output format from the video capturer. Captured frames
- // by the camera will be scaled/or dropped by the video capturer.
- // It does not matter if width and height are flipped. I.E, |width| = 640, |height| = 480 produce
- // the same result as |width| = 480, |height| = 640.
- // TODO(magjed/perkj): Document what this function does. Change name?
- @Override
- public void onOutputFormatRequest(final int width, final int height, final int framerate) {
- postOnCameraThread(new Runnable() {
- @Override
- public void run() {
- Logging.d(TAG,
- "onOutputFormatRequestOnCameraThread: " + width + "x" + height + "@" + framerate);
- capturerObserver.onOutputFormatRequest(width, height, framerate);
- }
- });
- }
-
- // Reconfigure the camera to capture in a new format. This should only be called while the camera
- // is running.
- @Override
- public void changeCaptureFormat(final int width, final int height, final int framerate) {
- synchronized (cameraStateLock) {
- waitForCameraToStartIfStarting();
-
- if (cameraState != CameraState.RUNNING) {
- Logging.e(TAG, "Calling changeCaptureFormat() on stopped camera.");
- return;
- }
-
- requestedWidth = width;
- requestedHeight = height;
- requestedFramerate = framerate;
- }
+ public Camera2Capturer(Context context, String cameraName, CameraEventsHandler eventsHandler) {
+ super(cameraName, eventsHandler, new Camera2Enumerator(context));
- // Make the switch.
- stopCapture();
- // TODO(magjed/sakal): Just recreate session.
- startCapture(width, height, framerate);
+ this.context = context;
+ cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
@Override
public List<CaptureFormat> getSupportedFormats() {
- synchronized (cameraState) {
- return Camera2Enumerator.getSupportedFormats(this.cameraManager, cameraName);
- }
- }
-
- @Override
- public void dispose() {
- synchronized (cameraStateLock) {
- waitForCameraToStopIfStopping();
-
- if (cameraState != CameraState.IDLE) {
- throw new IllegalStateException("Unexpected camera state for dispose: " + cameraState);
- }
- }
- }
-
- // Blocks until camera is known to be stopped.
- @Override
- public void stopCapture() {
- final CountDownLatch cameraStoppingLatch = new CountDownLatch(1);
-
- Logging.d(TAG, "stopCapture");
- checkNotOnCameraThread();
-
- synchronized (cameraStateLock) {
- waitForCameraToStartIfStarting();
-
- if (cameraState != CameraState.RUNNING) {
- Logging.w(TAG, "stopCapture called for already stopped camera.");
- return;
- }
-
- postOnCameraThread(new Runnable() {
- @Override
- public void run() {
- Logging.d(TAG, "stopCaptureOnCameraThread");
-
- // Stop capture.
- closeAndRelease();
- cameraStoppingLatch.countDown();
- }
- });
- }
-
- // Wait for the stopping to start
- ThreadUtils.awaitUninterruptibly(cameraStoppingLatch);
-
- Logging.d(TAG, "stopCapture done");
- }
-
- private void postOnCameraThread(Runnable runnable) {
- postDelayedOnCameraThread(0 /* delayMs */, runnable);
- }
-
- private void postDelayedOnCameraThread(int delayMs, Runnable runnable) {
- synchronized (cameraStateLock) {
- if ((cameraState != CameraState.STARTING && cameraState != CameraState.RUNNING)
- || !cameraThreadHandler.postAtTime(
- runnable, this /* token */, SystemClock.uptimeMillis() + delayMs)) {
- Logging.w(TAG, "Runnable not scheduled even though it was requested.");
- }
- }
- }
-
- private int getDeviceOrientation() {
- int orientation = 0;
-
- WindowManager wm = (WindowManager) applicationContext.getSystemService(
- Context.WINDOW_SERVICE);
- switch(wm.getDefaultDisplay().getRotation()) {
- case Surface.ROTATION_90:
- orientation = 90;
- break;
- case Surface.ROTATION_180:
- orientation = 180;
- break;
- case Surface.ROTATION_270:
- orientation = 270;
- break;
- case Surface.ROTATION_0:
- default:
- orientation = 0;
- break;
- }
- return orientation;
- }
-
- @Override
- public void onTextureFrameAvailable(
- int oesTextureId, float[] transformMatrix, long timestampNs) {
- checkIsOnCameraThread();
-
- if (cameraState != CameraState.RUNNING) {
- Logging.d(TAG, "Texture frame received while camera was not running.");
- return;
- }
-
- if (eventsHandler != null && !firstFrameReported) {
- eventsHandler.onFirstFrameAvailable();
- firstFrameReported = true;
- }
-
- int rotation;
- if (isFrontCamera) {
- // Undo the mirror that the OS "helps" us with.
- // http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
- rotation = cameraOrientation + getDeviceOrientation();
- transformMatrix =
- RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizontalFlipMatrix());
- } else {
- rotation = cameraOrientation - getDeviceOrientation();
- }
- // Make sure |rotation| is between 0 and 360.
- rotation = (360 + rotation % 360) % 360;
-
- // Undo camera orientation - we report it as rotation instead.
- transformMatrix = RendererCommon.rotateTextureMatrix(transformMatrix, -cameraOrientation);
-
- cameraStatistics.addFrame();
- capturerObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.height, oesTextureId,
- transformMatrix, rotation, timestampNs);
+ return Camera2Enumerator.getSupportedFormats(cameraManager, getCameraName());
+ }
+
+ @Override
+ protected void createCameraSession(
+ CameraSession.CreateSessionCallback createSessionCallback,
+ CameraEventsHandler eventsHandler, Context applicationContext,
+ CameraVideoCapturer.CapturerObserver capturerObserver,
+ SurfaceTextureHelper surfaceTextureHelper,
+ String cameraName, int width, int height, int framerate) {
+ Camera2Session.create(
+ cameraManager,
+ createSessionCallback,
+ eventsHandler, applicationContext,
+ capturerObserver,
+ surfaceTextureHelper,
+ cameraName, width, height, framerate);
}
}
« no previous file with comments | « webrtc/api/BUILD.gn ('k') | webrtc/api/android/java/src/org/webrtc/Camera2Session.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698