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);
listener = pendingListener;
pendingListener = null;
// May alredy have a pending frame - try delivering it.
tryDeliverTextureFrame();
// May have a pending frame from the previous capture session - drop it.
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);
}
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() {
if (handler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException("Wrong thread.");
@ -465,12 +478,7 @@ class SurfaceTextureHelper {
isTextureInUse = true;
hasPendingTexture = false;
// 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();
}
updateTexImage();
final float[] transformMatrix = new float[16];
surfaceTexture.getTransformMatrix(transformMatrix);

View File

@ -78,9 +78,6 @@ public class VideoCapturerAndroid implements
private final Set<byte[]> queuedBuffers = new HashSet<byte[]>();
private final boolean isCapturingToTexture;
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 OPEN_CAMERA_DELAY_MS = 500;
private int openCameraAttempts;
@ -462,7 +459,6 @@ public class VideoCapturerAndroid implements
// Temporarily stop preview if it's already running.
if (this.captureFormat != null) {
camera.stopPreview();
dropNextFrame = true;
// 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);
@ -564,7 +560,6 @@ public class VideoCapturerAndroid implements
synchronized (cameraIdLock) {
id = (id + 1) % android.hardware.Camera.getNumberOfCameras();
}
dropNextFrame = true;
startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate, frameObserver,
applicationContext);
Logging.d(TAG, "switchCameraOnCameraThread done");
@ -654,11 +649,6 @@ public class VideoCapturerAndroid implements
throw new RuntimeException("onTextureFrameAvailable() called after stopCapture().");
}
checkIsOnCameraThread();
if (dropNextFrame) {
surfaceHelper.returnTextureFrame();
dropNextFrame = false;
return;
}
if (eventsHandler != null && !firstFrameReported) {
eventsHandler.onFirstFrameAvailable();
firstFrameReported = true;