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

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

Issue 2168623002: Refactor stopCapture to be asynchronous in VideoCapturerAndroid. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@androidvideotracksource
Patch Set: Synchronized. Created 4 years, 5 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
Index: webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
diff --git a/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
index d3a9faff21b9bcb18c1daee086aedf3ec4305c0c..a60a5bde6e29c76c5991e78860568a169e116843 100644
--- a/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
+++ b/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
@@ -46,40 +46,55 @@ public class VideoCapturerAndroid implements
SurfaceTextureHelper.OnTextureFrameAvailableListener {
private static final String TAG = "VideoCapturerAndroid";
private static final int CAMERA_STOP_TIMEOUT_MS = 7000;
+ // 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 static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
+ private final static int OPEN_CAMERA_DELAY_MS = 500;
+ private static enum CameraState { UNINITIALIAZED, IDLE, STARTING, RUNNING }
- private android.hardware.Camera camera; // Only non-null while capturing.
- private final AtomicBoolean isCameraRunning = new AtomicBoolean();
- // Use maybePostOnCameraThread() instead of posting directly to the handler - this way all
+ private final Set<byte[]> queuedBuffers = new HashSet<byte[]>();
+ private final boolean isCapturingToTexture;
+ private final CameraEventsHandler eventsHandler;
+
+ // 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 volatile Handler cameraThreadHandler;
+ 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;
+ private CapturerObserver capturerObserver = null;
+ private SurfaceTextureHelper surfaceHelper;
+
+ // Internal state - will only be touched from the camera thread
+ // ------------------------------------------------------------
+ private android.hardware.Camera camera; // Only non-null while capturing.
// 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.
+ // Populated with the information of the active camera.
+ private android.hardware.Camera.CameraInfo info;
+ // Active capture format.
private CaptureFormat captureFormat;
- private final Object pendingCameraSwitchLock = new Object();
- private volatile boolean pendingCameraSwitch;
- private CapturerObserver frameObserver = null;
- private final CameraEventsHandler eventsHandler;
+ private CameraStatistics cameraStatistics;
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;
+ // Locked objects
+ // --------------
+ private final Object cameraIdLock = new Object();
+ private int id; // Only edited from camera thread while holding the lock.
+
+ private final Object cameraStateLock = new Object();
+ private CameraState cameraState = CameraState.UNINITIALIAZED;
+
+ // Only allow one camera switch a time.
+ private final Object cameraSwitchLock = new Object();
+ private boolean pendingCameraSwitch;
+ private CameraSwitchHandler switchEventsHandler;
+
// Camera error callback.
private final android.hardware.Camera.ErrorCallback cameraErrorCallback =
new android.hardware.Camera.ErrorCallback() {
@@ -98,6 +113,14 @@ public class VideoCapturerAndroid implements
}
};
+ private void setCameraState(CameraState newState) {
+ Logging.d(TAG, "Camera changing state from " + cameraState + " to " + newState);
+ synchronized (cameraStateLock) {
+ cameraState = newState;
+ cameraStateLock.notifyAll();
+ }
+ }
+
public static VideoCapturerAndroid create(String name,
CameraEventsHandler eventsHandler) {
return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTexture */);
@@ -134,14 +157,15 @@ public class VideoCapturerAndroid implements
// 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) {
+ public synchronized void switchCamera(final CameraSwitchHandler switchEventsHandler) {
if (android.hardware.Camera.getNumberOfCameras() < 2) {
if (switchEventsHandler != null) {
switchEventsHandler.onCameraSwitchError("No camera to switch to.");
}
return;
}
- synchronized (pendingCameraSwitchLock) {
+
+ synchronized (cameraSwitchLock) {
if (pendingCameraSwitch) {
// Do not handle multiple camera switch request to avoid blocking
// camera thread by handling too many switch request from a queue.
@@ -151,24 +175,24 @@ public class VideoCapturerAndroid implements
}
return;
}
+
pendingCameraSwitch = true;
}
- final boolean didPost = maybePostOnCameraThread(new Runnable() {
+
+ postOnCameraThread(new Runnable() {
@Override
public void run() {
+ // Set switchEventsHandler only here because now openCameraOnCameraThread cannot be called
+ // in-between these calls.
+ synchronized (cameraSwitchLock) {
+ VideoCapturerAndroid.this.switchEventsHandler = switchEventsHandler;
+ }
switchCameraOnCameraThread();
- synchronized (pendingCameraSwitchLock) {
+ synchronized (cameraSwitchLock) {
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
@@ -177,8 +201,9 @@ public class VideoCapturerAndroid implements
// 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() {
+ public synchronized void onOutputFormatRequest(
+ final int width, final int height, final int framerate) {
+ postOnCameraThread(new Runnable() {
@Override public void run() {
onOutputFormatRequestOnCameraThread(width, height, framerate);
}
@@ -188,25 +213,24 @@ public class VideoCapturerAndroid implements
// 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() {
+ public synchronized void changeCaptureFormat(
+ final int width, final int height, final int framerate) {
+ postOnCameraThread(new Runnable() {
@Override public void run() {
- startPreviewOnCameraThread(width, height, framerate);
+ requestedWidth = width;
+ requestedHeight = height;
+ requestedFramerate = framerate;
+
+ startPreviewOnCameraThread();
}
});
}
- // 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());
+ synchronized (cameraIdLock) {
+ return Camera1Enumerator.getSupportedFormats(id);
+ }
}
// Returns true if this VideoCapturer is setup to capture video frames to a SurfaceTexture.
@@ -231,117 +255,127 @@ public class VideoCapturerAndroid implements
private void checkIsOnCameraThread() {
if (cameraThreadHandler == null) {
- Logging.e(TAG, "Camera is not initialized - can't check thread.");
+ throw new IllegalStateException("Camera is not initialized - 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);
+ /**
+ * Return true if camera is initialized otherwise returns false and logs an error.
+ */
+ private boolean checkIsInitialized() {
+ synchronized (cameraStateLock) {
+ if (cameraState == CameraState.UNINITIALIAZED) {
+ Logging.e(TAG, "Calling methods on an uninitialized camera.");
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private void postOnCameraThread(Runnable runnable) {
+ postDelayedOnCameraThread(0 /* delayMs */, runnable);
}
- private boolean maybePostDelayedOnCameraThread(int delayMs, Runnable runnable) {
- return cameraThreadHandler != null && isCameraRunning.get()
- && cameraThreadHandler.postAtTime(
- runnable, this /* token */, SystemClock.uptimeMillis() + delayMs);
+ private void postDelayedOnCameraThread(int delayMs, Runnable runnable) {
+ if (!cameraThreadHandler.postAtTime(
+ runnable, this /* token */, SystemClock.uptimeMillis() + delayMs)) {
+ throw new RuntimeException("Failed to post a runnable on camera thread.");
+ }
}
@Override
public void dispose() {
Logging.d(TAG, "dispose");
- }
-
- private boolean isInitialized() {
- return applicationContext != null && frameObserver != null;
+ stopCapture(); // Stop the camera in case the caller forgot
}
@Override
public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext,
- CapturerObserver frameObserver) {
+ CapturerObserver capturerObserver) {
Logging.d(TAG, "initialize");
if (applicationContext == null) {
throw new IllegalArgumentException("applicationContext not set.");
}
- if (frameObserver == null) {
- throw new IllegalArgumentException("frameObserver not set.");
+ if (capturerObserver == null) {
+ throw new IllegalArgumentException("capturerObserver not set.");
}
- if (isInitialized()) {
+ if (cameraState != CameraState.UNINITIALIAZED) {
throw new IllegalStateException("Already initialized");
}
this.applicationContext = applicationContext;
- this.frameObserver = frameObserver;
+ this.capturerObserver = capturerObserver;
this.surfaceHelper = surfaceTextureHelper;
this.cameraThreadHandler =
surfaceTextureHelper == null ? null : surfaceTextureHelper.getHandler();
+
+ // Nothing should be running on the camera thread so should be safe.
+ setCameraState(CameraState.IDLE);
}
// 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) {
- Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + framerate);
- if (!isInitialized()) {
- throw new IllegalStateException("startCapture called in uninitialized state");
+ public synchronized void startCapture(final int width, final int height, final int framerate) {
+ if (!checkIsInitialized()) {
+ return;
}
+
+ Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + framerate);
+
if (surfaceHelper == null) {
- frameObserver.onCapturerStarted(false /* success */);
+ capturerObserver.onCapturerStarted(false /* success */);
if (eventsHandler != null) {
eventsHandler.onCameraError("No SurfaceTexture created.");
}
return;
}
- if (isCameraRunning.getAndSet(true)) {
- Logging.e(TAG, "Camera has already been started.");
- return;
- }
- final boolean didPost = maybePostOnCameraThread(new Runnable() {
+
+ postOnCameraThread(new Runnable() {
@Override
public void run() {
- openCameraAttempts = 0;
startCaptureOnCameraThread(width, height, framerate);
}
});
- if (!didPost) {
- frameObserver.onCapturerStarted(false);
- if (eventsHandler != null) {
- eventsHandler.onCameraError("Could not post task to camera thread.");
- }
- isCameraRunning.set(false);
- }
}
private void startCaptureOnCameraThread(final int width, final int height, final int framerate) {
checkIsOnCameraThread();
- if (!isCameraRunning.get()) {
- Logging.e(TAG, "startCaptureOnCameraThread: Camera is stopped");
- return;
- }
- if (camera != null) {
- Logging.e(TAG, "startCaptureOnCameraThread: Camera has already been started.");
+
+ if (cameraState != CameraState.IDLE) {
+ Logging.e(TAG, "Camera has already been started.");
return;
}
- this.firstFrameReported = false;
+ openCameraAttempts = 0;
+ firstFrameReported = false;
+ setCameraState(CameraState.STARTING);
+
+ requestedWidth = width;
+ requestedHeight = height;
+ requestedFramerate = framerate;
+
+ openCameraOnCameraThread();
+ }
+
+ private void openCameraOnCameraThread() {
try {
+ Logging.d(TAG, "Opening camera " + id);
+ if (eventsHandler != null) {
+ eventsHandler.onCameraOpening(id);
+ }
+
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);
- }
+ camera = android.hardware.Camera.open(id);
} 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() {
+ postDelayedOnCameraThread(OPEN_CAMERA_DELAY_MS, new Runnable() {
@Override
public void run() {
- startCaptureOnCameraThread(width, height, framerate);
+ openCameraOnCameraThread();
}
});
return;
@@ -349,43 +383,58 @@ public class VideoCapturerAndroid implements
throw e;
}
+ setCameraState(CameraState.RUNNING);
+
+ info = new android.hardware.Camera.CameraInfo();
+ android.hardware.Camera.getCameraInfo(id, info);
+
camera.setPreviewTexture(surfaceHelper.getSurfaceTexture());
Logging.d(TAG, "Camera orientation: " + info.orientation +
" .Device orientation: " + getDeviceOrientation());
camera.setErrorCallback(cameraErrorCallback);
- startPreviewOnCameraThread(width, height, framerate);
- frameObserver.onCapturerStarted(true);
+ startPreviewOnCameraThread();
+ capturerObserver.onCapturerStarted(true);
+ synchronized (cameraSwitchLock) {
+ if (switchEventsHandler != null) {
+ switchEventsHandler.onCameraSwitchDone(
+ info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
+ }
+ switchEventsHandler = null;
+ }
if (isCapturingToTexture) {
surfaceHelper.startListening(this);
}
- // Start camera observer.
+ // Start camera statistics collector.
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);
+ capturerObserver.onCapturerStarted(false);
+ setCameraState(CameraState.IDLE);
+ releaseCameraOnCameraThread();
if (eventsHandler != null) {
eventsHandler.onCameraError("Camera can not be started.");
}
- }
+ synchronized (cameraSwitchLock) {
+ if (switchEventsHandler != null) {
+ switchEventsHandler.onCameraSwitchError("Camera can not be started.");
+ }
+ switchEventsHandler = null;
+ }
+ }
}
// (Re)start preview with the closest supported format to |width| x |height| @ |framerate|.
- private void startPreviewOnCameraThread(int width, int height, int framerate) {
+ private void startPreviewOnCameraThread() {
checkIsOnCameraThread();
- if (!isCameraRunning.get() || camera == null) {
- Logging.e(TAG, "startPreviewOnCameraThread: Camera is stopped");
+ if (cameraState != CameraState.RUNNING) {
+ Logging.e(TAG, "startPreviewOnCameraThread: Camera is not running");
return;
}
- Logging.d(
- TAG, "startPreviewOnCameraThread requested: " + width + "x" + height + "@" + framerate);
-
- requestedWidth = width;
- requestedHeight = height;
- requestedFramerate = framerate;
+ Logging.d(TAG, "startPreviewOnCameraThread requested: "
+ + requestedWidth + "x" + requestedHeight + "@" + requestedFramerate);
// Find closest supported format for |width| x |height| @ |framerate|.
final android.hardware.Camera.Parameters parameters = camera.getParameters();
@@ -394,10 +443,12 @@ public class VideoCapturerAndroid implements
Logging.d(TAG, "Available fps ranges: " + supportedFramerates);
final CaptureFormat.FramerateRange fpsRange =
- CameraEnumerationAndroid.getClosestSupportedFramerateRange(supportedFramerates, framerate);
+ CameraEnumerationAndroid.getClosestSupportedFramerateRange(
+ supportedFramerates, requestedFramerate);
final Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize(
- Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()), width, height);
+ Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()),
+ requestedWidth, requestedHeight);
final CaptureFormat captureFormat =
new CaptureFormat(previewSize.width, previewSize.height, fpsRange);
@@ -426,7 +477,8 @@ public class VideoCapturerAndroid implements
// 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);
+ Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()),
+ requestedWidth, requestedHeight);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
// Temporarily stop preview if it's already running.
@@ -462,53 +514,90 @@ public class VideoCapturerAndroid implements
camera.startPreview();
}
- // Blocks until camera is known to be stopped.
+ // Blocks until camera is known to be stopped. Synchronized to allow only on stop capture call at
+ // a time.
@Override
- public void stopCapture() throws InterruptedException {
+ public synchronized void stopCapture() {
+ if (!checkIsInitialized()) {
+ return;
+ }
Logging.d(TAG, "stopCapture");
- final CountDownLatch barrier = new CountDownLatch(1);
- final boolean didPost = maybePostOnCameraThread(new Runnable() {
+
+ // Count down latch to ensure runnable has been started and it is not some other callback
+ // stopping the camera.
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ postOnCameraThread(new Runnable() {
@Override public void run() {
- stopCaptureOnCameraThread(true /* stopHandler */);
- barrier.countDown();
+ countDownLatch.countDown();
+ stopCaptureOnCameraThread();
}
});
- 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: Waiting for the stopping to start");
+ ThreadUtils.awaitUninterruptibly(countDownLatch);
+ Logging.d(TAG, "stopCapture: Waiting for the camera to stop");
+ synchronized (cameraStateLock) {
+ while (cameraState != CameraState.IDLE) {
+ try {
+ cameraStateLock.wait();
+ } catch (InterruptedException e) {
+ Logging.w(TAG, "Interrupt while waiting capturer to stop: "
+ + e.getMessage());
+ }
}
}
- frameObserver.onCapturerStopped();
+
Logging.d(TAG, "stopCapture done");
}
- private void stopCaptureOnCameraThread(boolean stopHandler) {
+ private void stopCaptureOnCameraThread() {
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) {
- // 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 |isCameraRunning|.
- // Remove all pending Runnables posted from |this|.
- isCameraRunning.set(false);
- cameraThreadHandler.removeCallbacksAndMessages(this /* token */);
+ if (cameraState == CameraState.IDLE) {
+ Logging.d(TAG, "Calling stopCapture() for already stopped camera.");
+ return;
+ }
+
+ final CameraState oldState = cameraState;
+
+ // 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 |isCameraRunning|.
+ // Remove all pending Runnables posted from |this|.
+ // Make sure no other callback starts the camera again after this
+ cameraThreadHandler.removeCallbacksAndMessages(this /* token */);
+ setCameraState(CameraState.IDLE); // No more frames will be delivered
+
+ if (oldState == CameraState.STARTING) {
+ Logging.d(TAG, "Camera starting while calling stopCapture, canceling.");
+ capturerObserver.onCapturerStarted(false);
+ setCameraState(CameraState.IDLE);
+ return;
}
+
+ capturerObserver.onCapturerStopped();
+
+ releaseCameraOnCameraThread();
+ if (eventsHandler != null) {
+ eventsHandler.onCameraClosed();
+ }
+
+ Logging.d(TAG, "stopCaptureOnCameraThread done");
+ }
+
+ // Note: stopPreview or other driver code might deadlock. Deadlock in
+ // android.hardware.Camera._stopPreview(Native Method) has
+ // been observed on Nexus 5 (hammerhead), OS version LMY48I.
+ private void releaseCameraOnCameraThread() {
+ checkIsOnCameraThread();
+
+ // Make sure onTextureFrameAvailable() is not called anymore.
+ surfaceHelper.stopListening();
if (cameraStatistics != null) {
cameraStatistics.release();
cameraStatistics = null;
@@ -520,30 +609,32 @@ public class VideoCapturerAndroid implements
}
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() {
checkIsOnCameraThread();
- if (!isCameraRunning.get()) {
+ Logging.d(TAG, "switchCameraOnCameraThread");
+
+ if (cameraState == CameraState.IDLE) {
Logging.e(TAG, "switchCameraOnCameraThread: Camera is stopped");
+ if (switchEventsHandler != null) {
+ switchEventsHandler.onCameraSwitchError("Camera is stopped.");
+ }
return;
}
- Logging.d(TAG, "switchCameraOnCameraThread");
- stopCaptureOnCameraThread(false /* stopHandler */);
+
+ stopCaptureOnCameraThread();
synchronized (cameraIdLock) {
id = (id + 1) % android.hardware.Camera.getNumberOfCameras();
}
- startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate);
+ startCaptureOnCameraThread(requestedWidth, requestedHeight,
+ requestedFramerate);
+
Logging.d(TAG, "switchCameraOnCameraThread done");
}
@@ -551,7 +642,7 @@ public class VideoCapturerAndroid implements
checkIsOnCameraThread();
Logging.d(TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + height +
"@" + framerate);
- frameObserver.onOutputFormatRequest(width, height, framerate);
+ capturerObserver.onOutputFormatRequest(width, height, framerate);
}
private int getDeviceOrientation() {
@@ -588,11 +679,12 @@ public class VideoCapturerAndroid implements
// Called on cameraThread so must not "synchronized".
@Override
public void onPreviewFrame(byte[] data, android.hardware.Camera callbackCamera) {
- checkIsOnCameraThread();
- if (!isCameraRunning.get()) {
- Logging.e(TAG, "onPreviewFrame: Camera is stopped");
+ if (cameraState != CameraState.RUNNING) {
+ Logging.d(TAG, "onPreviewFrame: Camera is stopped");
return;
}
+ checkIsOnCameraThread();
+
if (!queuedBuffers.contains(data)) {
// |data| is an old invalid buffer.
return;
@@ -610,7 +702,7 @@ public class VideoCapturerAndroid implements
}
cameraStatistics.addFrame();
- frameObserver.onByteBufferFrameCaptured(data, captureFormat.width, captureFormat.height,
+ capturerObserver.onByteBufferFrameCaptured(data, captureFormat.width, captureFormat.height,
getFrameOrientation(), captureTimeNs);
camera.addCallbackBuffer(data);
}
@@ -618,12 +710,13 @@ public class VideoCapturerAndroid implements
@Override
public void onTextureFrameAvailable(
int oesTextureId, float[] transformMatrix, long timestampNs) {
- checkIsOnCameraThread();
- if (!isCameraRunning.get()) {
- Logging.e(TAG, "onTextureFrameAvailable: Camera is stopped");
+ if (cameraState != CameraState.RUNNING) {
+ Logging.d(TAG, "onTextureFrameAvailable: Camera is stopped");
surfaceHelper.returnTextureFrame();
return;
}
+ checkIsOnCameraThread();
+
if (eventsHandler != null && !firstFrameReported) {
eventsHandler.onFirstFrameAvailable();
firstFrameReported = true;
@@ -637,7 +730,7 @@ public class VideoCapturerAndroid implements
RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizontalFlipMatrix());
}
cameraStatistics.addFrame();
- frameObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.height, oesTextureId,
+ capturerObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.height, oesTextureId,
transformMatrix, rotation, timestampNs);
}
}

Powered by Google App Engine
This is Rietveld 408576698