Android: Drop old frame in SurfaceTextureHelper.startListening()

Drop any pending texture frame when SurfaceTextureHelper.startListening()
is called because the frame might be from the previous
startListening()/stopListening() capture session. This typically happens
when switching between the front/back camera, and an old frame will get
incorrect rotation and mirroring because of the front/back camera
mismatch.

Dropping the frame in SurfaceTextureHelper also removes the need for
the |dropNextFrame| logic in VideoCapturerAndroid.

R=perkj@webrtc.org

Review URL: https://codereview.webrtc.org/2002963002 .

Cr-Commit-Position: refs/heads/master@{#12849}
This commit is contained in:
Magnus Jedvert
2016-05-23 16:26:47 +02:00
parent b432b26b5f
commit 181310fb6f
2 changed files with 16 additions and 18 deletions

View File

@ -309,8 +309,12 @@ class SurfaceTextureHelper {
Logging.d(TAG, "Setting listener to " + pendingListener); Logging.d(TAG, "Setting listener to " + pendingListener);
listener = pendingListener; listener = pendingListener;
pendingListener = null; pendingListener = null;
// May alredy have a pending frame - try delivering it. // May have a pending frame from the previous capture session - drop it.
tryDeliverTextureFrame(); if (hasPendingTexture) {
// Calling updateTexImage() is neccessary in order to receive new frames.
updateTexImage();
hasPendingTexture = false;
}
} }
}; };
@ -455,6 +459,15 @@ class SurfaceTextureHelper {
getYuvConverter().convert(buf, width, height, stride, textureId, transformMatrix); getYuvConverter().convert(buf, width, height, stride, textureId, transformMatrix);
} }
private void updateTexImage() {
// SurfaceTexture.updateTexImage apparently can compete and deadlock with eglSwapBuffers,
// as observed on Nexus 5. Therefore, synchronize it with the EGL functions.
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=5702 for more info.
synchronized (EglBase.lock) {
surfaceTexture.updateTexImage();
}
}
private void tryDeliverTextureFrame() { private void tryDeliverTextureFrame() {
if (handler.getLooper().getThread() != Thread.currentThread()) { if (handler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException("Wrong thread."); throw new IllegalStateException("Wrong thread.");
@ -465,12 +478,7 @@ class SurfaceTextureHelper {
isTextureInUse = true; isTextureInUse = true;
hasPendingTexture = false; hasPendingTexture = false;
// SurfaceTexture.updateTexImage apparently can compete and deadlock with eglSwapBuffers, updateTexImage();
// as observed on Nexus 5. Therefore, synchronize it with the EGL functions.
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=5702 for more info.
synchronized (EglBase.lock) {
surfaceTexture.updateTexImage();
}
final float[] transformMatrix = new float[16]; final float[] transformMatrix = new float[16];
surfaceTexture.getTransformMatrix(transformMatrix); surfaceTexture.getTransformMatrix(transformMatrix);

View File

@ -78,9 +78,6 @@ public class VideoCapturerAndroid implements
private final Set<byte[]> queuedBuffers = new HashSet<byte[]>(); private final Set<byte[]> queuedBuffers = new HashSet<byte[]>();
private final boolean isCapturingToTexture; private final boolean isCapturingToTexture;
private SurfaceTextureHelper surfaceHelper; private SurfaceTextureHelper surfaceHelper;
// The camera API can output one old frame after the camera has been switched or the resolution
// has been changed. This flag is used for dropping the first frame after camera restart.
private boolean dropNextFrame = false;
private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
private final static int OPEN_CAMERA_DELAY_MS = 500; private final static int OPEN_CAMERA_DELAY_MS = 500;
private int openCameraAttempts; private int openCameraAttempts;
@ -462,7 +459,6 @@ public class VideoCapturerAndroid implements
// Temporarily stop preview if it's already running. // Temporarily stop preview if it's already running.
if (this.captureFormat != null) { if (this.captureFormat != null) {
camera.stopPreview(); camera.stopPreview();
dropNextFrame = true;
// Calling |setPreviewCallbackWithBuffer| with null should clear the internal camera buffer // 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. // queue, but sometimes we receive a frame with the old resolution after this call anyway.
camera.setPreviewCallbackWithBuffer(null); camera.setPreviewCallbackWithBuffer(null);
@ -564,7 +560,6 @@ public class VideoCapturerAndroid implements
synchronized (cameraIdLock) { synchronized (cameraIdLock) {
id = (id + 1) % android.hardware.Camera.getNumberOfCameras(); id = (id + 1) % android.hardware.Camera.getNumberOfCameras();
} }
dropNextFrame = true;
startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate, frameObserver, startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate, frameObserver,
applicationContext); applicationContext);
Logging.d(TAG, "switchCameraOnCameraThread done"); Logging.d(TAG, "switchCameraOnCameraThread done");
@ -654,11 +649,6 @@ public class VideoCapturerAndroid implements
throw new RuntimeException("onTextureFrameAvailable() called after stopCapture()."); throw new RuntimeException("onTextureFrameAvailable() called after stopCapture().");
} }
checkIsOnCameraThread(); checkIsOnCameraThread();
if (dropNextFrame) {
surfaceHelper.returnTextureFrame();
dropNextFrame = false;
return;
}
if (eventsHandler != null && !firstFrameReported) { if (eventsHandler != null && !firstFrameReported) {
eventsHandler.onFirstFrameAvailable(); eventsHandler.onFirstFrameAvailable();
firstFrameReported = true; firstFrameReported = true;