| Index: webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
 | 
| diff --git a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
 | 
| deleted file mode 100644
 | 
| index 8f6f911b1da3c2c7a19035b6da2509ed020bd948..0000000000000000000000000000000000000000
 | 
| --- a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
 | 
| +++ /dev/null
 | 
| @@ -1,672 +0,0 @@
 | 
| -/*
 | 
| - *  Copyright 2015 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 android.view.Surface;
 | 
| -import android.view.WindowManager;
 | 
| -
 | 
| -import java.io.IOException;
 | 
| -import java.nio.ByteBuffer;
 | 
| -import java.util.HashSet;
 | 
| -import java.util.List;
 | 
| -import java.util.Set;
 | 
| -import java.util.concurrent.CountDownLatch;
 | 
| -import java.util.concurrent.TimeUnit;
 | 
| -
 | 
| -// Android specific implementation of VideoCapturer.
 | 
| -// An instance of this class can be created by an application using
 | 
| -// VideoCapturerAndroid.create();
 | 
| -// This class extends VideoCapturer with a method to easily switch between the
 | 
| -// front and back camera. It also provides methods for enumerating valid device
 | 
| -// names.
 | 
| -//
 | 
| -// Threading notes: this class is called from C++ code, Android Camera callbacks, and possibly
 | 
| -// arbitrary Java threads. All public entry points are thread safe, and delegate the work to the
 | 
| -// camera thread. The internal *OnCameraThread() methods must check |camera| for null to check if
 | 
| -// the camera has been stopped.
 | 
| -// TODO(magjed): This class name is now confusing - rename to Camera1VideoCapturer.
 | 
| -@SuppressWarnings("deprecation")
 | 
| -public class VideoCapturerAndroid implements
 | 
| -    CameraVideoCapturer,
 | 
| -    android.hardware.Camera.PreviewCallback,
 | 
| -    SurfaceTextureHelper.OnTextureFrameAvailableListener {
 | 
| -  private final static String TAG = "VideoCapturerAndroid";
 | 
| -  private static final int CAMERA_STOP_TIMEOUT_MS = 7000;
 | 
| -
 | 
| -  private android.hardware.Camera camera;  // Only non-null while capturing.
 | 
| -  private final Object handlerLock = new Object();
 | 
| -  // |cameraThreadHandler| must be synchronized on |handlerLock| when not on the camera thread,
 | 
| -  // or when modifying the reference. Use maybePostOnCameraThread() 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;
 | 
| -  // Synchronization lock for |id|.
 | 
| -  private final Object cameraIdLock = new Object();
 | 
| -  private int id;
 | 
| -  private android.hardware.Camera.CameraInfo info;
 | 
| -  private CameraStatistics cameraStatistics;
 | 
| -  // Remember the requested format in case we want to switch cameras.
 | 
| -  private int requestedWidth;
 | 
| -  private int requestedHeight;
 | 
| -  private int requestedFramerate;
 | 
| -  // The capture format will be the closest supported format to the requested format.
 | 
| -  private CaptureFormat captureFormat;
 | 
| -  private final Object pendingCameraSwitchLock = new Object();
 | 
| -  private volatile boolean pendingCameraSwitch;
 | 
| -  private CapturerObserver frameObserver = null;
 | 
| -  private final CameraEventsHandler eventsHandler;
 | 
| -  private boolean firstFrameReported;
 | 
| -  // Arbitrary queue depth.  Higher number means more memory allocated & held,
 | 
| -  // lower number means more sensitivity to processing time in the client (and
 | 
| -  // potentially stalling the capturer if it runs out of buffers to write to).
 | 
| -  private static final int NUMBER_OF_CAPTURE_BUFFERS = 3;
 | 
| -  private final Set<byte[]> queuedBuffers = new HashSet<byte[]>();
 | 
| -  private final boolean isCapturingToTexture;
 | 
| -  private SurfaceTextureHelper surfaceHelper;
 | 
| -  private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
 | 
| -  private final static int OPEN_CAMERA_DELAY_MS = 500;
 | 
| -  private int openCameraAttempts;
 | 
| -
 | 
| -  // Camera error callback.
 | 
| -  private final android.hardware.Camera.ErrorCallback cameraErrorCallback =
 | 
| -      new android.hardware.Camera.ErrorCallback() {
 | 
| -    @Override
 | 
| -    public void onError(int error, android.hardware.Camera camera) {
 | 
| -      String errorMessage;
 | 
| -      if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
 | 
| -        errorMessage = "Camera server died!";
 | 
| -      } else {
 | 
| -        errorMessage = "Camera error: " + error;
 | 
| -      }
 | 
| -      Logging.e(TAG, errorMessage);
 | 
| -      if (eventsHandler != null) {
 | 
| -        eventsHandler.onCameraError(errorMessage);
 | 
| -      }
 | 
| -    }
 | 
| -  };
 | 
| -
 | 
| -  public static VideoCapturerAndroid create(String name,
 | 
| -      CameraEventsHandler eventsHandler) {
 | 
| -    return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTexture */);
 | 
| -  }
 | 
| -
 | 
| -  // Use ctor directly instead.
 | 
| -  @Deprecated
 | 
| -  public static VideoCapturerAndroid create(String name,
 | 
| -      CameraEventsHandler eventsHandler, boolean captureToTexture) {
 | 
| -    try {
 | 
| -      return new VideoCapturerAndroid(name, eventsHandler, captureToTexture);
 | 
| -    } catch (RuntimeException e) {
 | 
| -      Logging.e(TAG, "Couldn't create camera.", e);
 | 
| -      return null;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  public void printStackTrace() {
 | 
| -    Thread cameraThread = null;
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler != null) {
 | 
| -        cameraThread = cameraThreadHandler.getLooper().getThread();
 | 
| -      }
 | 
| -    }
 | 
| -    if (cameraThread != null) {
 | 
| -      StackTraceElement[] cameraStackTraces = cameraThread.getStackTrace();
 | 
| -      if (cameraStackTraces.length > 0) {
 | 
| -        Logging.d(TAG, "VideoCapturerAndroid stacks trace:");
 | 
| -        for (StackTraceElement stackTrace : cameraStackTraces) {
 | 
| -          Logging.d(TAG, stackTrace.toString());
 | 
| -        }
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // 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) {
 | 
| -    if (android.hardware.Camera.getNumberOfCameras() < 2) {
 | 
| -      if (switchEventsHandler != null) {
 | 
| -        switchEventsHandler.onCameraSwitchError("No camera to switch to.");
 | 
| -      }
 | 
| -      return;
 | 
| -    }
 | 
| -    synchronized (pendingCameraSwitchLock) {
 | 
| -      if (pendingCameraSwitch) {
 | 
| -        // Do not handle multiple camera switch request to avoid blocking
 | 
| -        // camera thread by handling too many switch request from a queue.
 | 
| -        Logging.w(TAG, "Ignoring camera switch request.");
 | 
| -        if (switchEventsHandler != null) {
 | 
| -          switchEventsHandler.onCameraSwitchError("Pending camera switch already in progress.");
 | 
| -        }
 | 
| -        return;
 | 
| -      }
 | 
| -      pendingCameraSwitch = true;
 | 
| -    }
 | 
| -    final boolean didPost = maybePostOnCameraThread(new Runnable() {
 | 
| -      @Override
 | 
| -      public void run() {
 | 
| -        switchCameraOnCameraThread();
 | 
| -        synchronized (pendingCameraSwitchLock) {
 | 
| -          pendingCameraSwitch = false;
 | 
| -        }
 | 
| -        if (switchEventsHandler != null) {
 | 
| -          switchEventsHandler.onCameraSwitchDone(
 | 
| -              info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
 | 
| -        }
 | 
| -      }
 | 
| -    });
 | 
| -    if (!didPost && switchEventsHandler != null) {
 | 
| -      switchEventsHandler.onCameraSwitchError("Camera is stopped.");
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // 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) {
 | 
| -    maybePostOnCameraThread(new Runnable() {
 | 
| -      @Override public void run() {
 | 
| -        onOutputFormatRequestOnCameraThread(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) {
 | 
| -    maybePostOnCameraThread(new Runnable() {
 | 
| -      @Override public void run() {
 | 
| -        startPreviewOnCameraThread(width, height, framerate);
 | 
| -      }
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  // Helper function to retrieve the current camera id synchronously. Note that the camera id might
 | 
| -  // change at any point by switchCamera() calls.
 | 
| -  private int getCurrentCameraId() {
 | 
| -    synchronized (cameraIdLock) {
 | 
| -      return id;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  @Override
 | 
| -  public List<CaptureFormat> getSupportedFormats() {
 | 
| -    return Camera1Enumerator.getSupportedFormats(getCurrentCameraId());
 | 
| -  }
 | 
| -
 | 
| -  // Returns true if this VideoCapturer is setup to capture video frames to a SurfaceTexture.
 | 
| -  public boolean isCapturingToTexture() {
 | 
| -    return isCapturingToTexture;
 | 
| -  }
 | 
| -
 | 
| -  public VideoCapturerAndroid(String cameraName, CameraEventsHandler eventsHandler,
 | 
| -      boolean captureToTexture) {
 | 
| -    if (android.hardware.Camera.getNumberOfCameras() == 0) {
 | 
| -      throw new RuntimeException("No cameras available");
 | 
| -    }
 | 
| -    if (cameraName == null || cameraName.equals("")) {
 | 
| -      this.id = 0;
 | 
| -    } else {
 | 
| -      this.id = Camera1Enumerator.getCameraIndex(cameraName);
 | 
| -    }
 | 
| -    this.eventsHandler = eventsHandler;
 | 
| -    isCapturingToTexture = captureToTexture;
 | 
| -    Logging.d(TAG, "VideoCapturerAndroid isCapturingToTexture : " + isCapturingToTexture);
 | 
| -  }
 | 
| -
 | 
| -  private void checkIsOnCameraThread() {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "Camera is stopped - can't check thread.");
 | 
| -      } else if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) {
 | 
| -        throw new IllegalStateException("Wrong thread");
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  private boolean maybePostOnCameraThread(Runnable runnable) {
 | 
| -    return maybePostDelayedOnCameraThread(0 /* delayMs */, runnable);
 | 
| -  }
 | 
| -
 | 
| -  private boolean maybePostDelayedOnCameraThread(int delayMs, Runnable runnable) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      return cameraThreadHandler != null
 | 
| -          && cameraThreadHandler.postAtTime(
 | 
| -              runnable, this /* token */, SystemClock.uptimeMillis() + delayMs);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  @Override
 | 
| -  public void dispose() {
 | 
| -    Logging.d(TAG, "dispose");
 | 
| -  }
 | 
| -
 | 
| -  // Note that this actually opens the camera, and Camera callbacks run on the
 | 
| -  // thread that calls open(), so this is done on the CameraThread.
 | 
| -  @Override
 | 
| -  public void startCapture(
 | 
| -      final int width, final int height, final int framerate,
 | 
| -      final SurfaceTextureHelper surfaceTextureHelper, final Context applicationContext,
 | 
| -      final CapturerObserver frameObserver) {
 | 
| -    Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + framerate);
 | 
| -    if (surfaceTextureHelper == null) {
 | 
| -      frameObserver.onCapturerStarted(false /* success */);
 | 
| -      if (eventsHandler != null) {
 | 
| -        eventsHandler.onCameraError("No SurfaceTexture created.");
 | 
| -      }
 | 
| -      return;
 | 
| -    }
 | 
| -    if (applicationContext == null) {
 | 
| -      throw new IllegalArgumentException("applicationContext not set.");
 | 
| -    }
 | 
| -    if (frameObserver == null) {
 | 
| -      throw new IllegalArgumentException("frameObserver not set.");
 | 
| -    }
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (this.cameraThreadHandler != null) {
 | 
| -        throw new RuntimeException("Camera has already been started.");
 | 
| -      }
 | 
| -      this.cameraThreadHandler = surfaceTextureHelper.getHandler();
 | 
| -      this.surfaceHelper = surfaceTextureHelper;
 | 
| -      final boolean didPost = maybePostOnCameraThread(new Runnable() {
 | 
| -        @Override
 | 
| -        public void run() {
 | 
| -          openCameraAttempts = 0;
 | 
| -          startCaptureOnCameraThread(width, height, framerate, frameObserver,
 | 
| -              applicationContext);
 | 
| -        }
 | 
| -      });
 | 
| -      if (!didPost) {
 | 
| -        frameObserver.onCapturerStarted(false);
 | 
| -        if (eventsHandler != null) {
 | 
| -          eventsHandler.onCameraError("Could not post task to camera thread.");
 | 
| -        }
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  private void startCaptureOnCameraThread(
 | 
| -      final int width, final int height, final int framerate, final CapturerObserver frameObserver,
 | 
| -      final Context applicationContext) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "startCaptureOnCameraThread: Camera is stopped");
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    if (camera != null) {
 | 
| -      Logging.e(TAG, "startCaptureOnCameraThread: Camera has already been started.");
 | 
| -      return;
 | 
| -    }
 | 
| -    this.applicationContext = applicationContext;
 | 
| -    this.frameObserver = frameObserver;
 | 
| -    this.firstFrameReported = false;
 | 
| -
 | 
| -    try {
 | 
| -      try {
 | 
| -        synchronized (cameraIdLock) {
 | 
| -          Logging.d(TAG, "Opening camera " + id);
 | 
| -          if (eventsHandler != null) {
 | 
| -            eventsHandler.onCameraOpening(id);
 | 
| -          }
 | 
| -          camera = android.hardware.Camera.open(id);
 | 
| -          info = new android.hardware.Camera.CameraInfo();
 | 
| -          android.hardware.Camera.getCameraInfo(id, info);
 | 
| -        }
 | 
| -      } catch (RuntimeException e) {
 | 
| -        openCameraAttempts++;
 | 
| -        if (openCameraAttempts < MAX_OPEN_CAMERA_ATTEMPTS) {
 | 
| -          Logging.e(TAG, "Camera.open failed, retrying", e);
 | 
| -          maybePostDelayedOnCameraThread(OPEN_CAMERA_DELAY_MS, new Runnable() {
 | 
| -            @Override public void run() {
 | 
| -              startCaptureOnCameraThread(width, height, framerate, frameObserver,
 | 
| -                  applicationContext);
 | 
| -            }
 | 
| -          });
 | 
| -          return;
 | 
| -        }
 | 
| -        throw e;
 | 
| -      }
 | 
| -
 | 
| -      camera.setPreviewTexture(surfaceHelper.getSurfaceTexture());
 | 
| -
 | 
| -      Logging.d(TAG, "Camera orientation: " + info.orientation +
 | 
| -          " .Device orientation: " + getDeviceOrientation());
 | 
| -      camera.setErrorCallback(cameraErrorCallback);
 | 
| -      startPreviewOnCameraThread(width, height, framerate);
 | 
| -      frameObserver.onCapturerStarted(true);
 | 
| -      if (isCapturingToTexture) {
 | 
| -        surfaceHelper.startListening(this);
 | 
| -      }
 | 
| -
 | 
| -      // Start camera observer.
 | 
| -      cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler);
 | 
| -    } catch (IOException|RuntimeException e) {
 | 
| -      Logging.e(TAG, "startCapture failed", e);
 | 
| -      // Make sure the camera is released.
 | 
| -      stopCaptureOnCameraThread(true /* stopHandler */);
 | 
| -      frameObserver.onCapturerStarted(false);
 | 
| -      if (eventsHandler != null) {
 | 
| -        eventsHandler.onCameraError("Camera can not be started.");
 | 
| -      }
 | 
| -     }
 | 
| -  }
 | 
| -
 | 
| -  // (Re)start preview with the closest supported format to |width| x |height| @ |framerate|.
 | 
| -  private void startPreviewOnCameraThread(int width, int height, int framerate) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null || camera == null) {
 | 
| -        Logging.e(TAG, "startPreviewOnCameraThread: Camera is stopped");
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    Logging.d(
 | 
| -        TAG, "startPreviewOnCameraThread requested: " + width + "x" + height + "@" + framerate);
 | 
| -
 | 
| -    requestedWidth = width;
 | 
| -    requestedHeight = height;
 | 
| -    requestedFramerate = framerate;
 | 
| -
 | 
| -    // Find closest supported format for |width| x |height| @ |framerate|.
 | 
| -    final android.hardware.Camera.Parameters parameters = camera.getParameters();
 | 
| -    final List<CaptureFormat.FramerateRange> supportedFramerates =
 | 
| -        Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRange());
 | 
| -    Logging.d(TAG, "Available fps ranges: " + supportedFramerates);
 | 
| -
 | 
| -    final CaptureFormat.FramerateRange fpsRange =
 | 
| -        CameraEnumerationAndroid.getClosestSupportedFramerateRange(supportedFramerates, framerate);
 | 
| -
 | 
| -    final Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize(
 | 
| -        Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()), width, height);
 | 
| -
 | 
| -    final CaptureFormat captureFormat =
 | 
| -        new CaptureFormat(previewSize.width, previewSize.height, fpsRange);
 | 
| -
 | 
| -    // Check if we are already using this capture format, then we don't need to do anything.
 | 
| -    if (captureFormat.equals(this.captureFormat)) {
 | 
| -      return;
 | 
| -    }
 | 
| -
 | 
| -    // Update camera parameters.
 | 
| -    Logging.d(TAG, "isVideoStabilizationSupported: " +
 | 
| -        parameters.isVideoStabilizationSupported());
 | 
| -    if (parameters.isVideoStabilizationSupported()) {
 | 
| -      parameters.setVideoStabilization(true);
 | 
| -    }
 | 
| -    // Note: setRecordingHint(true) actually decrease frame rate on N5.
 | 
| -    // parameters.setRecordingHint(true);
 | 
| -    if (captureFormat.framerate.max > 0) {
 | 
| -      parameters.setPreviewFpsRange(captureFormat.framerate.min, captureFormat.framerate.max);
 | 
| -    }
 | 
| -    parameters.setPreviewSize(previewSize.width, previewSize.height);
 | 
| -
 | 
| -    if (!isCapturingToTexture) {
 | 
| -      parameters.setPreviewFormat(captureFormat.imageFormat);
 | 
| -    }
 | 
| -    // Picture size is for taking pictures and not for preview/video, but we need to set it anyway
 | 
| -    // as a workaround for an aspect ratio problem on Nexus 7.
 | 
| -    final Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize(
 | 
| -        Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), width, height);
 | 
| -    parameters.setPictureSize(pictureSize.width, pictureSize.height);
 | 
| -
 | 
| -    // Temporarily stop preview if it's already running.
 | 
| -    if (this.captureFormat != null) {
 | 
| -      camera.stopPreview();
 | 
| -      // Calling |setPreviewCallbackWithBuffer| with null should clear the internal camera buffer
 | 
| -      // queue, but sometimes we receive a frame with the old resolution after this call anyway.
 | 
| -      camera.setPreviewCallbackWithBuffer(null);
 | 
| -    }
 | 
| -
 | 
| -    // (Re)start preview.
 | 
| -    Logging.d(TAG, "Start capturing: " + captureFormat);
 | 
| -    this.captureFormat = captureFormat;
 | 
| -
 | 
| -    List<String> focusModes = parameters.getSupportedFocusModes();
 | 
| -    if (focusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
 | 
| -      parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
 | 
| -    }
 | 
| -
 | 
| -    camera.setParameters(parameters);
 | 
| -    // Calculate orientation manually and send it as CVO instead.
 | 
| -    camera.setDisplayOrientation(0 /* degrees */);
 | 
| -    if (!isCapturingToTexture) {
 | 
| -      queuedBuffers.clear();
 | 
| -      final int frameSize = captureFormat.frameSize();
 | 
| -      for (int i = 0; i < NUMBER_OF_CAPTURE_BUFFERS; ++i) {
 | 
| -        final ByteBuffer buffer = ByteBuffer.allocateDirect(frameSize);
 | 
| -        queuedBuffers.add(buffer.array());
 | 
| -        camera.addCallbackBuffer(buffer.array());
 | 
| -      }
 | 
| -      camera.setPreviewCallbackWithBuffer(this);
 | 
| -    }
 | 
| -    camera.startPreview();
 | 
| -  }
 | 
| -
 | 
| -  // Blocks until camera is known to be stopped.
 | 
| -  @Override
 | 
| -  public void stopCapture() throws InterruptedException {
 | 
| -    Logging.d(TAG, "stopCapture");
 | 
| -    final CountDownLatch barrier = new CountDownLatch(1);
 | 
| -    final boolean didPost = maybePostOnCameraThread(new Runnable() {
 | 
| -      @Override public void run() {
 | 
| -        stopCaptureOnCameraThread(true /* stopHandler */);
 | 
| -        barrier.countDown();
 | 
| -      }
 | 
| -    });
 | 
| -    if (!didPost) {
 | 
| -      Logging.e(TAG, "Calling stopCapture() for already stopped camera.");
 | 
| -      return;
 | 
| -    }
 | 
| -    if (!barrier.await(CAMERA_STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
 | 
| -      Logging.e(TAG, "Camera stop timeout");
 | 
| -      printStackTrace();
 | 
| -      if (eventsHandler != null) {
 | 
| -        eventsHandler.onCameraError("Camera stop timeout");
 | 
| -      }
 | 
| -    }
 | 
| -    Logging.d(TAG, "stopCapture done");
 | 
| -  }
 | 
| -
 | 
| -  private void stopCaptureOnCameraThread(boolean stopHandler) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "stopCaptureOnCameraThread: Camera is stopped");
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    Logging.d(TAG, "stopCaptureOnCameraThread");
 | 
| -    // Note that the camera might still not be started here if startCaptureOnCameraThread failed
 | 
| -    // and we posted a retry.
 | 
| -
 | 
| -    // Make sure onTextureFrameAvailable() is not called anymore.
 | 
| -    if (surfaceHelper != null) {
 | 
| -      surfaceHelper.stopListening();
 | 
| -    }
 | 
| -    if (stopHandler) {
 | 
| -      synchronized (handlerLock) {
 | 
| -        // Clear the cameraThreadHandler first, in case stopPreview or
 | 
| -        // other driver code deadlocks. Deadlock in
 | 
| -        // android.hardware.Camera._stopPreview(Native Method) has
 | 
| -        // been observed on Nexus 5 (hammerhead), OS version LMY48I.
 | 
| -        // The camera might post another one or two preview frames
 | 
| -        // before stopped, so we have to check for a null
 | 
| -        // cameraThreadHandler in our handler. Remove all pending
 | 
| -        // Runnables posted from |this|.
 | 
| -        if (cameraThreadHandler != null) {
 | 
| -          cameraThreadHandler.removeCallbacksAndMessages(this /* token */);
 | 
| -          cameraThreadHandler = null;
 | 
| -        }
 | 
| -        surfaceHelper = null;
 | 
| -      }
 | 
| -    }
 | 
| -    if (cameraStatistics != null) {
 | 
| -      cameraStatistics.release();
 | 
| -      cameraStatistics = null;
 | 
| -    }
 | 
| -    Logging.d(TAG, "Stop preview.");
 | 
| -    if (camera != null) {
 | 
| -      camera.stopPreview();
 | 
| -      camera.setPreviewCallbackWithBuffer(null);
 | 
| -    }
 | 
| -    queuedBuffers.clear();
 | 
| -    captureFormat = null;
 | 
| -
 | 
| -    Logging.d(TAG, "Release camera.");
 | 
| -    if (camera != null) {
 | 
| -      camera.release();
 | 
| -      camera = null;
 | 
| -    }
 | 
| -    if (eventsHandler != null) {
 | 
| -      eventsHandler.onCameraClosed();
 | 
| -    }
 | 
| -    Logging.d(TAG, "stopCaptureOnCameraThread done");
 | 
| -  }
 | 
| -
 | 
| -  private void switchCameraOnCameraThread() {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "switchCameraOnCameraThread: Camera is stopped");
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    Logging.d(TAG, "switchCameraOnCameraThread");
 | 
| -    stopCaptureOnCameraThread(false /* stopHandler */);
 | 
| -    synchronized (cameraIdLock) {
 | 
| -      id = (id + 1) % android.hardware.Camera.getNumberOfCameras();
 | 
| -    }
 | 
| -    startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate, frameObserver,
 | 
| -        applicationContext);
 | 
| -    Logging.d(TAG, "switchCameraOnCameraThread done");
 | 
| -  }
 | 
| -
 | 
| -  private void onOutputFormatRequestOnCameraThread(int width, int height, int framerate) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null || camera == null) {
 | 
| -        Logging.e(TAG, "onOutputFormatRequestOnCameraThread: Camera is stopped");
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    Logging.d(TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + height +
 | 
| -        "@" + framerate);
 | 
| -    frameObserver.onOutputFormatRequest(width, height, framerate);
 | 
| -  }
 | 
| -
 | 
| -  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;
 | 
| -  }
 | 
| -
 | 
| -  private int getFrameOrientation() {
 | 
| -    int rotation = getDeviceOrientation();
 | 
| -    if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
 | 
| -      rotation = 360 - rotation;
 | 
| -    }
 | 
| -    return (info.orientation + rotation) % 360;
 | 
| -  }
 | 
| -
 | 
| -  // Called on cameraThread so must not "synchronized".
 | 
| -  @Override
 | 
| -  public void onPreviewFrame(byte[] data, android.hardware.Camera callbackCamera) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "onPreviewFrame: Camera is stopped");
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    if (!queuedBuffers.contains(data)) {
 | 
| -      // |data| is an old invalid buffer.
 | 
| -      return;
 | 
| -    }
 | 
| -    if (camera != callbackCamera) {
 | 
| -      throw new RuntimeException("Unexpected camera in callback!");
 | 
| -    }
 | 
| -
 | 
| -    final long captureTimeNs =
 | 
| -        TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
 | 
| -
 | 
| -    if (eventsHandler != null && !firstFrameReported) {
 | 
| -      eventsHandler.onFirstFrameAvailable();
 | 
| -      firstFrameReported = true;
 | 
| -    }
 | 
| -
 | 
| -    cameraStatistics.addFrame();
 | 
| -    frameObserver.onByteBufferFrameCaptured(data, captureFormat.width, captureFormat.height,
 | 
| -        getFrameOrientation(), captureTimeNs);
 | 
| -    camera.addCallbackBuffer(data);
 | 
| -  }
 | 
| -
 | 
| -  @Override
 | 
| -  public void onTextureFrameAvailable(
 | 
| -      int oesTextureId, float[] transformMatrix, long timestampNs) {
 | 
| -    synchronized (handlerLock) {
 | 
| -      if (cameraThreadHandler == null) {
 | 
| -        Logging.e(TAG, "onTextureFrameAvailable: Camera is stopped");
 | 
| -        surfaceHelper.returnTextureFrame();
 | 
| -        return;
 | 
| -      } else {
 | 
| -        checkIsOnCameraThread();
 | 
| -      }
 | 
| -    }
 | 
| -    if (eventsHandler != null && !firstFrameReported) {
 | 
| -      eventsHandler.onFirstFrameAvailable();
 | 
| -      firstFrameReported = true;
 | 
| -    }
 | 
| -
 | 
| -    int rotation = getFrameOrientation();
 | 
| -    if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) {
 | 
| -      // Undo the mirror that the OS "helps" us with.
 | 
| -      // http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
 | 
| -      transformMatrix =
 | 
| -          RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizontalFlipMatrix());
 | 
| -    }
 | 
| -    cameraStatistics.addFrame();
 | 
| -    frameObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.height, oesTextureId,
 | 
| -        transformMatrix, rotation, timestampNs);
 | 
| -  }
 | 
| -}
 | 
| 
 |