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 |
index b8ab1a8c5ea6223ce46aebf7dae8a43e8fd21241..c41aa0991874487ab62e3e9f4960e23057b02368 100644 |
--- a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java |
+++ b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java |
@@ -11,7 +11,11 @@ |
package org.webrtc; |
import android.content.Context; |
+import android.graphics.SurfaceTexture; |
+import android.opengl.GLES11Ext; |
+import android.opengl.GLES20; |
import android.os.Handler; |
+import android.os.HandlerThread; |
import android.os.SystemClock; |
import android.view.Surface; |
import android.view.WindowManager; |
@@ -77,8 +81,11 @@ public class VideoCapturerAndroid implements |
// 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 boolean isCapturingToTexture; |
private SurfaceTextureHelper surfaceHelper; |
+ // These dummy variables are used in case |surfaceHelper| is null. |
+ private int dummyTextureId; |
+ private SurfaceTexture dummySurfaceTexture; |
// 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; |
@@ -119,7 +126,7 @@ public class VideoCapturerAndroid implements |
if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount >= CAMERA_FREEZE_REPORT_TIMOUT_MS |
&& eventsHandler != null) { |
Logging.e(TAG, "Camera freezed."); |
- if (surfaceHelper.isTextureInUse()) { |
+ if (surfaceHelper != null && surfaceHelper.isTextureInUse()) { |
// This can only happen if we are capturing to textures. |
eventsHandler.onCameraFreezed("Camera failure. Client must return video buffers."); |
} else { |
@@ -374,9 +381,6 @@ public class VideoCapturerAndroid implements |
final SurfaceTextureHelper surfaceTextureHelper, final Context applicationContext, |
final CapturerObserver frameObserver) { |
Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + framerate); |
- if (surfaceTextureHelper == null) { |
- throw new IllegalArgumentException("surfaceTextureHelper not set."); |
- } |
if (applicationContext == null) { |
throw new IllegalArgumentException("applicationContext not set."); |
} |
@@ -387,8 +391,20 @@ public class VideoCapturerAndroid implements |
if (this.cameraThreadHandler != null) { |
throw new RuntimeException("Camera has already been started."); |
} |
- this.cameraThreadHandler = surfaceTextureHelper.getHandler(); |
- this.surfaceHelper = surfaceTextureHelper; |
+ if (surfaceTextureHelper != null) { |
+ this.cameraThreadHandler = surfaceTextureHelper.getHandler(); |
+ this.surfaceHelper = surfaceTextureHelper; |
+ } else { |
+ // No SurfaceTextureHelper - create own handler. |
+ if (isCapturingToTexture) { |
+ Logging.e(TAG, "No SurfaceTextureHelper - falling back to byte buffer capture"); |
+ isCapturingToTexture = false; |
+ } |
+ this.surfaceHelper = null; |
+ final HandlerThread thread = new HandlerThread(TAG); |
+ thread.start(); |
+ this.cameraThreadHandler = new Handler(thread.getLooper()); |
+ } |
final boolean didPost = maybePostOnCameraThread(new Runnable() { |
@Override |
public void run() { |
@@ -445,7 +461,14 @@ public class VideoCapturerAndroid implements |
} |
try { |
- camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
+ if (surfaceHelper != null) { |
+ camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
+ } else { |
+ // No SurfaceTextureHelper - create own dummy SurfaceTexture. |
+ this.dummyTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); |
+ this.dummySurfaceTexture = new SurfaceTexture(dummyTextureId); |
+ camera.setPreviewTexture(dummySurfaceTexture); |
+ } |
} catch (IOException e) { |
Logging.e(TAG, "setPreviewTexture failed", error); |
throw new RuntimeException(e); |
@@ -456,7 +479,7 @@ public class VideoCapturerAndroid implements |
camera.setErrorCallback(cameraErrorCallback); |
startPreviewOnCameraThread(width, height, framerate); |
frameObserver.onCapturerStarted(true); |
- if (isCapturingToTexture) { |
+ if (isCapturingToTexture && surfaceHelper != null) { |
surfaceHelper.startListening(this); |
} |
@@ -474,6 +497,7 @@ public class VideoCapturerAndroid implements |
synchronized (handlerLock) { |
// Remove all pending Runnables posted from |this|. |
cameraThreadHandler.removeCallbacksAndMessages(this /* token */); |
+ releaseOwnHandlerAndDummySurfaceTexture(); |
cameraThreadHandler = null; |
} |
frameObserver.onCapturerStarted(false); |
@@ -483,6 +507,22 @@ public class VideoCapturerAndroid implements |
return; |
} |
+ private void releaseOwnHandlerAndDummySurfaceTexture() { |
+ if (surfaceHelper == null) { |
+ // We are using our own thread - quit it. |
+ cameraThreadHandler.getLooper().quit(); |
+ cameraThreadHandler = null; |
+ } |
+ if (dummyTextureId != 0) { |
+ GLES20.glDeleteTextures(1, new int[] {dummyTextureId}, 0); |
+ dummyTextureId = 0; |
+ } |
+ if (dummySurfaceTexture != null) { |
+ dummySurfaceTexture.release(); |
+ dummySurfaceTexture = null; |
+ } |
+ } |
+ |
// (Re)start preview with the closest supported format to |width| x |height| @ |framerate|. |
private void startPreviewOnCameraThread(int width, int height, int framerate) { |
checkIsOnCameraThread(); |
@@ -584,6 +624,7 @@ public class VideoCapturerAndroid implements |
synchronized (handlerLock) { |
// Remove all pending Runnables posted from |this|. |
cameraThreadHandler.removeCallbacksAndMessages(this /* token */); |
+ releaseOwnHandlerAndDummySurfaceTexture(); |
cameraThreadHandler = null; |
surfaceHelper = null; |
} |
@@ -609,7 +650,9 @@ public class VideoCapturerAndroid implements |
Logging.d(TAG, "stopCaptureOnCameraThread"); |
// Make sure onTextureFrameAvailable() is not called anymore. |
- surfaceHelper.stopListening(); |
+ if (surfaceHelper != null) { |
+ surfaceHelper.stopListening(); |
+ } |
cameraThreadHandler.removeCallbacks(cameraObserver); |
cameraStatistics.getAndResetFrameCount(); |
Logging.d(TAG, "Stop preview."); |