| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 // camera thread. The internal *OnCameraThread() methods must check |camera| for
null to check if | 39 // camera thread. The internal *OnCameraThread() methods must check |camera| for
null to check if |
| 40 // the camera has been stopped. | 40 // the camera has been stopped. |
| 41 // TODO(magjed): This class name is now confusing - rename to Camera1VideoCaptur
er. | 41 // TODO(magjed): This class name is now confusing - rename to Camera1VideoCaptur
er. |
| 42 @SuppressWarnings("deprecation") | 42 @SuppressWarnings("deprecation") |
| 43 public class VideoCapturerAndroid implements | 43 public class VideoCapturerAndroid implements |
| 44 CameraVideoCapturer, | 44 CameraVideoCapturer, |
| 45 android.hardware.Camera.PreviewCallback, | 45 android.hardware.Camera.PreviewCallback, |
| 46 SurfaceTextureHelper.OnTextureFrameAvailableListener { | 46 SurfaceTextureHelper.OnTextureFrameAvailableListener { |
| 47 private static final String TAG = "VideoCapturerAndroid"; | 47 private static final String TAG = "VideoCapturerAndroid"; |
| 48 private static final int CAMERA_STOP_TIMEOUT_MS = 7000; | 48 private static final int CAMERA_STOP_TIMEOUT_MS = 7000; |
| 49 // Arbitrary queue depth. Higher number means more memory allocated & held, |
| 50 // lower number means more sensitivity to processing time in the client (and |
| 51 // potentially stalling the capturer if it runs out of buffers to write to). |
| 52 private static final int NUMBER_OF_CAPTURE_BUFFERS = 3; |
| 53 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; |
| 54 private final static int OPEN_CAMERA_DELAY_MS = 500; |
| 55 private static enum CameraState { UNINITIALIAZED, IDLE, STARTING, RUNNING } |
| 49 | 56 |
| 57 private final Set<byte[]> queuedBuffers = new HashSet<byte[]>(); |
| 58 private final boolean isCapturingToTexture; |
| 59 private final CameraEventsHandler eventsHandler; |
| 60 |
| 61 // Initialized on initialize |
| 62 // ------------------------- |
| 63 // Use postOnCameraThread() instead of posting directly to the handler - this
way all |
| 64 // callbacks with a specifed token can be removed at once. |
| 65 private Handler cameraThreadHandler; |
| 66 private Context applicationContext; |
| 67 private CapturerObserver capturerObserver = null; |
| 68 private SurfaceTextureHelper surfaceHelper; |
| 69 |
| 70 // Internal state - will only be touched from the camera thread |
| 71 // ------------------------------------------------------------ |
| 50 private android.hardware.Camera camera; // Only non-null while capturing. | 72 private android.hardware.Camera camera; // Only non-null while capturing. |
| 51 private final AtomicBoolean isCameraRunning = new AtomicBoolean(); | |
| 52 // Use maybePostOnCameraThread() instead of posting directly to the handler -
this way all | |
| 53 // callbacks with a specifed token can be removed at once. | |
| 54 private volatile Handler cameraThreadHandler; | |
| 55 private Context applicationContext; | |
| 56 // Synchronization lock for |id|. | |
| 57 private final Object cameraIdLock = new Object(); | |
| 58 private int id; | |
| 59 private android.hardware.Camera.CameraInfo info; | |
| 60 private CameraStatistics cameraStatistics; | |
| 61 // Remember the requested format in case we want to switch cameras. | 73 // Remember the requested format in case we want to switch cameras. |
| 62 private int requestedWidth; | 74 private int requestedWidth; |
| 63 private int requestedHeight; | 75 private int requestedHeight; |
| 64 private int requestedFramerate; | 76 private int requestedFramerate; |
| 65 // The capture format will be the closest supported format to the requested fo
rmat. | 77 // Populated with the information of the active camera. |
| 78 private android.hardware.Camera.CameraInfo info; |
| 79 // Active capture format. |
| 66 private CaptureFormat captureFormat; | 80 private CaptureFormat captureFormat; |
| 67 private final Object pendingCameraSwitchLock = new Object(); | 81 private CameraStatistics cameraStatistics; |
| 68 private volatile boolean pendingCameraSwitch; | |
| 69 private CapturerObserver frameObserver = null; | |
| 70 private final CameraEventsHandler eventsHandler; | |
| 71 private boolean firstFrameReported; | 82 private boolean firstFrameReported; |
| 72 // Arbitrary queue depth. Higher number means more memory allocated & held, | |
| 73 // lower number means more sensitivity to processing time in the client (and | |
| 74 // potentially stalling the capturer if it runs out of buffers to write to). | |
| 75 private static final int NUMBER_OF_CAPTURE_BUFFERS = 3; | |
| 76 private final Set<byte[]> queuedBuffers = new HashSet<byte[]>(); | |
| 77 private final boolean isCapturingToTexture; | |
| 78 private SurfaceTextureHelper surfaceHelper; | |
| 79 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; | |
| 80 private final static int OPEN_CAMERA_DELAY_MS = 500; | |
| 81 private int openCameraAttempts; | 83 private int openCameraAttempts; |
| 82 | 84 |
| 85 // Locked objects |
| 86 // -------------- |
| 87 private final Object cameraIdLock = new Object(); |
| 88 private int id; // Only edited from camera thread while holding the lock. |
| 89 |
| 90 private final Object cameraStateLock = new Object(); |
| 91 private CameraState cameraState = CameraState.UNINITIALIAZED; |
| 92 |
| 93 // Only allow one camera switch a time. |
| 94 private final Object cameraSwitchLock = new Object(); |
| 95 private boolean pendingCameraSwitch; |
| 96 private CameraSwitchHandler switchEventsHandler; |
| 97 |
| 83 // Camera error callback. | 98 // Camera error callback. |
| 84 private final android.hardware.Camera.ErrorCallback cameraErrorCallback = | 99 private final android.hardware.Camera.ErrorCallback cameraErrorCallback = |
| 85 new android.hardware.Camera.ErrorCallback() { | 100 new android.hardware.Camera.ErrorCallback() { |
| 86 @Override | 101 @Override |
| 87 public void onError(int error, android.hardware.Camera camera) { | 102 public void onError(int error, android.hardware.Camera camera) { |
| 88 String errorMessage; | 103 String errorMessage; |
| 89 if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) { | 104 if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) { |
| 90 errorMessage = "Camera server died!"; | 105 errorMessage = "Camera server died!"; |
| 91 } else { | 106 } else { |
| 92 errorMessage = "Camera error: " + error; | 107 errorMessage = "Camera error: " + error; |
| 93 } | 108 } |
| 94 Logging.e(TAG, errorMessage); | 109 Logging.e(TAG, errorMessage); |
| 95 if (eventsHandler != null) { | 110 if (eventsHandler != null) { |
| 96 eventsHandler.onCameraError(errorMessage); | 111 eventsHandler.onCameraError(errorMessage); |
| 97 } | 112 } |
| 98 } | 113 } |
| 99 }; | 114 }; |
| 100 | 115 |
| 116 private void setCameraState(CameraState newState) { |
| 117 Logging.d(TAG, "Camera changing state from " + cameraState + " to " + newSta
te); |
| 118 synchronized (cameraStateLock) { |
| 119 cameraState = newState; |
| 120 cameraStateLock.notifyAll(); |
| 121 } |
| 122 } |
| 123 |
| 101 public static VideoCapturerAndroid create(String name, | 124 public static VideoCapturerAndroid create(String name, |
| 102 CameraEventsHandler eventsHandler) { | 125 CameraEventsHandler eventsHandler) { |
| 103 return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTe
xture */); | 126 return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTe
xture */); |
| 104 } | 127 } |
| 105 | 128 |
| 106 // Use ctor directly instead. | 129 // Use ctor directly instead. |
| 107 @Deprecated | 130 @Deprecated |
| 108 public static VideoCapturerAndroid create(String name, | 131 public static VideoCapturerAndroid create(String name, |
| 109 CameraEventsHandler eventsHandler, boolean captureToTexture) { | 132 CameraEventsHandler eventsHandler, boolean captureToTexture) { |
| 110 try { | 133 try { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 127 for (StackTraceElement stackTrace : cameraStackTraces) { | 150 for (StackTraceElement stackTrace : cameraStackTraces) { |
| 128 Logging.d(TAG, stackTrace.toString()); | 151 Logging.d(TAG, stackTrace.toString()); |
| 129 } | 152 } |
| 130 } | 153 } |
| 131 } | 154 } |
| 132 } | 155 } |
| 133 | 156 |
| 134 // Switch camera to the next valid camera id. This can only be called while | 157 // Switch camera to the next valid camera id. This can only be called while |
| 135 // the camera is running. | 158 // the camera is running. |
| 136 @Override | 159 @Override |
| 137 public void switchCamera(final CameraSwitchHandler switchEventsHandler) { | 160 public synchronized void switchCamera(final CameraSwitchHandler switchEventsHa
ndler) { |
| 138 if (android.hardware.Camera.getNumberOfCameras() < 2) { | 161 if (android.hardware.Camera.getNumberOfCameras() < 2) { |
| 139 if (switchEventsHandler != null) { | 162 if (switchEventsHandler != null) { |
| 140 switchEventsHandler.onCameraSwitchError("No camera to switch to."); | 163 switchEventsHandler.onCameraSwitchError("No camera to switch to."); |
| 141 } | 164 } |
| 142 return; | 165 return; |
| 143 } | 166 } |
| 144 synchronized (pendingCameraSwitchLock) { | 167 |
| 168 synchronized (cameraSwitchLock) { |
| 145 if (pendingCameraSwitch) { | 169 if (pendingCameraSwitch) { |
| 146 // Do not handle multiple camera switch request to avoid blocking | 170 // Do not handle multiple camera switch request to avoid blocking |
| 147 // camera thread by handling too many switch request from a queue. | 171 // camera thread by handling too many switch request from a queue. |
| 148 Logging.w(TAG, "Ignoring camera switch request."); | 172 Logging.w(TAG, "Ignoring camera switch request."); |
| 149 if (switchEventsHandler != null) { | 173 if (switchEventsHandler != null) { |
| 150 switchEventsHandler.onCameraSwitchError("Pending camera switch already
in progress."); | 174 switchEventsHandler.onCameraSwitchError("Pending camera switch already
in progress."); |
| 151 } | 175 } |
| 152 return; | 176 return; |
| 153 } | 177 } |
| 178 |
| 154 pendingCameraSwitch = true; | 179 pendingCameraSwitch = true; |
| 155 } | 180 } |
| 156 final boolean didPost = maybePostOnCameraThread(new Runnable() { | 181 |
| 182 postOnCameraThread(new Runnable() { |
| 157 @Override | 183 @Override |
| 158 public void run() { | 184 public void run() { |
| 185 // Set switchEventsHandler only here because now openCameraOnCameraThrea
d cannot be called |
| 186 // in-between these calls. |
| 187 synchronized (cameraSwitchLock) { |
| 188 VideoCapturerAndroid.this.switchEventsHandler = switchEventsHandler; |
| 189 } |
| 159 switchCameraOnCameraThread(); | 190 switchCameraOnCameraThread(); |
| 160 synchronized (pendingCameraSwitchLock) { | 191 synchronized (cameraSwitchLock) { |
| 161 pendingCameraSwitch = false; | 192 pendingCameraSwitch = false; |
| 162 } | 193 } |
| 163 if (switchEventsHandler != null) { | |
| 164 switchEventsHandler.onCameraSwitchDone( | |
| 165 info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FR
ONT); | |
| 166 } | |
| 167 } | 194 } |
| 168 }); | 195 }); |
| 169 if (!didPost && switchEventsHandler != null) { | |
| 170 switchEventsHandler.onCameraSwitchError("Camera is stopped."); | |
| 171 } | |
| 172 } | 196 } |
| 173 | 197 |
| 174 // Requests a new output format from the video capturer. Captured frames | 198 // Requests a new output format from the video capturer. Captured frames |
| 175 // by the camera will be scaled/or dropped by the video capturer. | 199 // by the camera will be scaled/or dropped by the video capturer. |
| 176 // It does not matter if width and height are flipped. I.E, |width| = 640, |he
ight| = 480 produce | 200 // It does not matter if width and height are flipped. I.E, |width| = 640, |he
ight| = 480 produce |
| 177 // the same result as |width| = 480, |height| = 640. | 201 // the same result as |width| = 480, |height| = 640. |
| 178 // TODO(magjed/perkj): Document what this function does. Change name? | 202 // TODO(magjed/perkj): Document what this function does. Change name? |
| 179 @Override | 203 @Override |
| 180 public void onOutputFormatRequest(final int width, final int height, final int
framerate) { | 204 public synchronized void onOutputFormatRequest( |
| 181 maybePostOnCameraThread(new Runnable() { | 205 final int width, final int height, final int framerate) { |
| 206 postOnCameraThread(new Runnable() { |
| 182 @Override public void run() { | 207 @Override public void run() { |
| 183 onOutputFormatRequestOnCameraThread(width, height, framerate); | 208 onOutputFormatRequestOnCameraThread(width, height, framerate); |
| 184 } | 209 } |
| 185 }); | 210 }); |
| 186 } | 211 } |
| 187 | 212 |
| 188 // Reconfigure the camera to capture in a new format. This should only be call
ed while the camera | 213 // Reconfigure the camera to capture in a new format. This should only be call
ed while the camera |
| 189 // is running. | 214 // is running. |
| 190 @Override | 215 @Override |
| 191 public void changeCaptureFormat(final int width, final int height, final int f
ramerate) { | 216 public synchronized void changeCaptureFormat( |
| 192 maybePostOnCameraThread(new Runnable() { | 217 final int width, final int height, final int framerate) { |
| 218 postOnCameraThread(new Runnable() { |
| 193 @Override public void run() { | 219 @Override public void run() { |
| 194 startPreviewOnCameraThread(width, height, framerate); | 220 requestedWidth = width; |
| 221 requestedHeight = height; |
| 222 requestedFramerate = framerate; |
| 223 |
| 224 startPreviewOnCameraThread(); |
| 195 } | 225 } |
| 196 }); | 226 }); |
| 197 } | 227 } |
| 198 | 228 |
| 199 // Helper function to retrieve the current camera id synchronously. Note that
the camera id might | 229 @Override |
| 200 // change at any point by switchCamera() calls. | 230 public List<CaptureFormat> getSupportedFormats() { |
| 201 private int getCurrentCameraId() { | |
| 202 synchronized (cameraIdLock) { | 231 synchronized (cameraIdLock) { |
| 203 return id; | 232 return Camera1Enumerator.getSupportedFormats(id); |
| 204 } | 233 } |
| 205 } | 234 } |
| 206 | 235 |
| 207 @Override | |
| 208 public List<CaptureFormat> getSupportedFormats() { | |
| 209 return Camera1Enumerator.getSupportedFormats(getCurrentCameraId()); | |
| 210 } | |
| 211 | |
| 212 // Returns true if this VideoCapturer is setup to capture video frames to a Su
rfaceTexture. | 236 // Returns true if this VideoCapturer is setup to capture video frames to a Su
rfaceTexture. |
| 213 public boolean isCapturingToTexture() { | 237 public boolean isCapturingToTexture() { |
| 214 return isCapturingToTexture; | 238 return isCapturingToTexture; |
| 215 } | 239 } |
| 216 | 240 |
| 217 public VideoCapturerAndroid(String cameraName, CameraEventsHandler eventsHandl
er, | 241 public VideoCapturerAndroid(String cameraName, CameraEventsHandler eventsHandl
er, |
| 218 boolean captureToTexture) { | 242 boolean captureToTexture) { |
| 219 if (android.hardware.Camera.getNumberOfCameras() == 0) { | 243 if (android.hardware.Camera.getNumberOfCameras() == 0) { |
| 220 throw new RuntimeException("No cameras available"); | 244 throw new RuntimeException("No cameras available"); |
| 221 } | 245 } |
| 222 if (cameraName == null || cameraName.equals("")) { | 246 if (cameraName == null || cameraName.equals("")) { |
| 223 this.id = 0; | 247 this.id = 0; |
| 224 } else { | 248 } else { |
| 225 this.id = Camera1Enumerator.getCameraIndex(cameraName); | 249 this.id = Camera1Enumerator.getCameraIndex(cameraName); |
| 226 } | 250 } |
| 227 this.eventsHandler = eventsHandler; | 251 this.eventsHandler = eventsHandler; |
| 228 isCapturingToTexture = captureToTexture; | 252 isCapturingToTexture = captureToTexture; |
| 229 Logging.d(TAG, "VideoCapturerAndroid isCapturingToTexture : " + isCapturingT
oTexture); | 253 Logging.d(TAG, "VideoCapturerAndroid isCapturingToTexture : " + isCapturingT
oTexture); |
| 230 } | 254 } |
| 231 | 255 |
| 232 private void checkIsOnCameraThread() { | 256 private void checkIsOnCameraThread() { |
| 233 if (cameraThreadHandler == null) { | 257 if (cameraThreadHandler == null) { |
| 234 Logging.e(TAG, "Camera is not initialized - can't check thread."); | 258 throw new IllegalStateException("Camera is not initialized - can't check t
hread."); |
| 235 } else if (Thread.currentThread() != cameraThreadHandler.getLooper().getThre
ad()) { | 259 } else if (Thread.currentThread() != cameraThreadHandler.getLooper().getThre
ad()) { |
| 236 throw new IllegalStateException("Wrong thread"); | 260 throw new IllegalStateException("Wrong thread"); |
| 237 } | 261 } |
| 238 } | 262 } |
| 239 | 263 |
| 240 private boolean maybePostOnCameraThread(Runnable runnable) { | 264 /** |
| 241 return maybePostDelayedOnCameraThread(0 /* delayMs */, runnable); | 265 * Return true if camera is initialized otherwise returns false and logs an er
ror. |
| 266 */ |
| 267 private boolean checkIsInitialized() { |
| 268 synchronized (cameraStateLock) { |
| 269 if (cameraState == CameraState.UNINITIALIAZED) { |
| 270 Logging.e(TAG, "Calling methods on an uninitialized camera."); |
| 271 return false; |
| 272 } |
| 273 return true; |
| 274 } |
| 242 } | 275 } |
| 243 | 276 |
| 244 private boolean maybePostDelayedOnCameraThread(int delayMs, Runnable runnable)
{ | 277 private void postOnCameraThread(Runnable runnable) { |
| 245 return cameraThreadHandler != null && isCameraRunning.get() | 278 postDelayedOnCameraThread(0 /* delayMs */, runnable); |
| 246 && cameraThreadHandler.postAtTime( | 279 } |
| 247 runnable, this /* token */, SystemClock.uptimeMillis() + delayMs); | 280 |
| 281 private void postDelayedOnCameraThread(int delayMs, Runnable runnable) { |
| 282 if (!cameraThreadHandler.postAtTime( |
| 283 runnable, this /* token */, SystemClock.uptimeMillis() + delayMs)) { |
| 284 throw new RuntimeException("Failed to post a runnable on camera thread."); |
| 285 } |
| 248 } | 286 } |
| 249 | 287 |
| 250 @Override | 288 @Override |
| 251 public void dispose() { | 289 public void dispose() { |
| 252 Logging.d(TAG, "dispose"); | 290 Logging.d(TAG, "dispose"); |
| 253 } | 291 stopCapture(); // Stop the camera in case the caller forgot |
| 254 | |
| 255 private boolean isInitialized() { | |
| 256 return applicationContext != null && frameObserver != null; | |
| 257 } | 292 } |
| 258 | 293 |
| 259 @Override | 294 @Override |
| 260 public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context appl
icationContext, | 295 public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context appl
icationContext, |
| 261 CapturerObserver frameObserver) { | 296 CapturerObserver capturerObserver) { |
| 262 Logging.d(TAG, "initialize"); | 297 Logging.d(TAG, "initialize"); |
| 263 if (applicationContext == null) { | 298 if (applicationContext == null) { |
| 264 throw new IllegalArgumentException("applicationContext not set."); | 299 throw new IllegalArgumentException("applicationContext not set."); |
| 265 } | 300 } |
| 266 if (frameObserver == null) { | 301 if (capturerObserver == null) { |
| 267 throw new IllegalArgumentException("frameObserver not set."); | 302 throw new IllegalArgumentException("capturerObserver not set."); |
| 268 } | 303 } |
| 269 if (isInitialized()) { | 304 if (cameraState != CameraState.UNINITIALIAZED) { |
| 270 throw new IllegalStateException("Already initialized"); | 305 throw new IllegalStateException("Already initialized"); |
| 271 } | 306 } |
| 272 this.applicationContext = applicationContext; | 307 this.applicationContext = applicationContext; |
| 273 this.frameObserver = frameObserver; | 308 this.capturerObserver = capturerObserver; |
| 274 this.surfaceHelper = surfaceTextureHelper; | 309 this.surfaceHelper = surfaceTextureHelper; |
| 275 this.cameraThreadHandler = | 310 this.cameraThreadHandler = |
| 276 surfaceTextureHelper == null ? null : surfaceTextureHelper.getHandler(); | 311 surfaceTextureHelper == null ? null : surfaceTextureHelper.getHandler(); |
| 312 |
| 313 // Nothing should be running on the camera thread so should be safe. |
| 314 setCameraState(CameraState.IDLE); |
| 277 } | 315 } |
| 278 | 316 |
| 279 // Note that this actually opens the camera, and Camera callbacks run on the | 317 // Note that this actually opens the camera, and Camera callbacks run on the |
| 280 // thread that calls open(), so this is done on the CameraThread. | 318 // thread that calls open(), so this is done on the CameraThread. |
| 281 @Override | 319 @Override |
| 282 public void startCapture(final int width, final int height, final int framerat
e) { | 320 public synchronized void startCapture(final int width, final int height, final
int framerate) { |
| 321 if (!checkIsInitialized()) { |
| 322 return; |
| 323 } |
| 324 |
| 283 Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + fra
merate); | 325 Logging.d(TAG, "startCapture requested: " + width + "x" + height + "@" + fra
merate); |
| 284 if (!isInitialized()) { | 326 |
| 285 throw new IllegalStateException("startCapture called in uninitialized stat
e"); | |
| 286 } | |
| 287 if (surfaceHelper == null) { | 327 if (surfaceHelper == null) { |
| 288 frameObserver.onCapturerStarted(false /* success */); | 328 capturerObserver.onCapturerStarted(false /* success */); |
| 289 if (eventsHandler != null) { | 329 if (eventsHandler != null) { |
| 290 eventsHandler.onCameraError("No SurfaceTexture created."); | 330 eventsHandler.onCameraError("No SurfaceTexture created."); |
| 291 } | 331 } |
| 292 return; | 332 return; |
| 293 } | 333 } |
| 294 if (isCameraRunning.getAndSet(true)) { | 334 |
| 295 Logging.e(TAG, "Camera has already been started."); | 335 postOnCameraThread(new Runnable() { |
| 296 return; | |
| 297 } | |
| 298 final boolean didPost = maybePostOnCameraThread(new Runnable() { | |
| 299 @Override | 336 @Override |
| 300 public void run() { | 337 public void run() { |
| 301 openCameraAttempts = 0; | |
| 302 startCaptureOnCameraThread(width, height, framerate); | 338 startCaptureOnCameraThread(width, height, framerate); |
| 303 } | 339 } |
| 304 }); | 340 }); |
| 305 if (!didPost) { | |
| 306 frameObserver.onCapturerStarted(false); | |
| 307 if (eventsHandler != null) { | |
| 308 eventsHandler.onCameraError("Could not post task to camera thread."); | |
| 309 } | |
| 310 isCameraRunning.set(false); | |
| 311 } | |
| 312 } | 341 } |
| 313 | 342 |
| 314 private void startCaptureOnCameraThread(final int width, final int height, fin
al int framerate) { | 343 private void startCaptureOnCameraThread(final int width, final int height, fin
al int framerate) { |
| 315 checkIsOnCameraThread(); | 344 checkIsOnCameraThread(); |
| 316 if (!isCameraRunning.get()) { | 345 |
| 317 Logging.e(TAG, "startCaptureOnCameraThread: Camera is stopped"); | 346 if (cameraState != CameraState.IDLE) { |
| 347 Logging.e(TAG, "Camera has already been started."); |
| 318 return; | 348 return; |
| 319 } | 349 } |
| 320 if (camera != null) { | |
| 321 Logging.e(TAG, "startCaptureOnCameraThread: Camera has already been starte
d."); | |
| 322 return; | |
| 323 } | |
| 324 this.firstFrameReported = false; | |
| 325 | 350 |
| 351 openCameraAttempts = 0; |
| 352 firstFrameReported = false; |
| 353 setCameraState(CameraState.STARTING); |
| 354 |
| 355 requestedWidth = width; |
| 356 requestedHeight = height; |
| 357 requestedFramerate = framerate; |
| 358 |
| 359 openCameraOnCameraThread(); |
| 360 } |
| 361 |
| 362 private void openCameraOnCameraThread() { |
| 326 try { | 363 try { |
| 364 Logging.d(TAG, "Opening camera " + id); |
| 365 if (eventsHandler != null) { |
| 366 eventsHandler.onCameraOpening(id); |
| 367 } |
| 368 |
| 327 try { | 369 try { |
| 328 synchronized (cameraIdLock) { | 370 camera = android.hardware.Camera.open(id); |
| 329 Logging.d(TAG, "Opening camera " + id); | |
| 330 if (eventsHandler != null) { | |
| 331 eventsHandler.onCameraOpening(id); | |
| 332 } | |
| 333 camera = android.hardware.Camera.open(id); | |
| 334 info = new android.hardware.Camera.CameraInfo(); | |
| 335 android.hardware.Camera.getCameraInfo(id, info); | |
| 336 } | |
| 337 } catch (RuntimeException e) { | 371 } catch (RuntimeException e) { |
| 338 openCameraAttempts++; | 372 openCameraAttempts++; |
| 339 if (openCameraAttempts < MAX_OPEN_CAMERA_ATTEMPTS) { | 373 if (openCameraAttempts < MAX_OPEN_CAMERA_ATTEMPTS) { |
| 340 Logging.e(TAG, "Camera.open failed, retrying", e); | 374 Logging.e(TAG, "Camera.open failed, retrying", e); |
| 341 maybePostDelayedOnCameraThread(OPEN_CAMERA_DELAY_MS, new Runnable() { | 375 postDelayedOnCameraThread(OPEN_CAMERA_DELAY_MS, new Runnable() { |
| 342 @Override | 376 @Override |
| 343 public void run() { | 377 public void run() { |
| 344 startCaptureOnCameraThread(width, height, framerate); | 378 openCameraOnCameraThread(); |
| 345 } | 379 } |
| 346 }); | 380 }); |
| 347 return; | 381 return; |
| 348 } | 382 } |
| 349 throw e; | 383 throw e; |
| 350 } | 384 } |
| 351 | 385 |
| 386 setCameraState(CameraState.RUNNING); |
| 387 |
| 388 info = new android.hardware.Camera.CameraInfo(); |
| 389 android.hardware.Camera.getCameraInfo(id, info); |
| 390 |
| 352 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); | 391 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
| 353 | 392 |
| 354 Logging.d(TAG, "Camera orientation: " + info.orientation + | 393 Logging.d(TAG, "Camera orientation: " + info.orientation + |
| 355 " .Device orientation: " + getDeviceOrientation()); | 394 " .Device orientation: " + getDeviceOrientation()); |
| 356 camera.setErrorCallback(cameraErrorCallback); | 395 camera.setErrorCallback(cameraErrorCallback); |
| 357 startPreviewOnCameraThread(width, height, framerate); | 396 startPreviewOnCameraThread(); |
| 358 frameObserver.onCapturerStarted(true); | 397 capturerObserver.onCapturerStarted(true); |
| 398 synchronized (cameraSwitchLock) { |
| 399 if (switchEventsHandler != null) { |
| 400 switchEventsHandler.onCameraSwitchDone( |
| 401 info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FR
ONT); |
| 402 } |
| 403 switchEventsHandler = null; |
| 404 } |
| 359 if (isCapturingToTexture) { | 405 if (isCapturingToTexture) { |
| 360 surfaceHelper.startListening(this); | 406 surfaceHelper.startListening(this); |
| 361 } | 407 } |
| 362 | 408 |
| 363 // Start camera observer. | 409 // Start camera statistics collector. |
| 364 cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler); | 410 cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler); |
| 365 } catch (IOException|RuntimeException e) { | 411 } catch (IOException|RuntimeException e) { |
| 366 Logging.e(TAG, "startCapture failed", e); | 412 Logging.e(TAG, "startCapture failed", e); |
| 367 // Make sure the camera is released. | 413 // Make sure the camera is released. |
| 368 stopCaptureOnCameraThread(true /* stopHandler */); | 414 capturerObserver.onCapturerStarted(false); |
| 369 frameObserver.onCapturerStarted(false); | 415 setCameraState(CameraState.IDLE); |
| 416 releaseCameraOnCameraThread(); |
| 370 if (eventsHandler != null) { | 417 if (eventsHandler != null) { |
| 371 eventsHandler.onCameraError("Camera can not be started."); | 418 eventsHandler.onCameraError("Camera can not be started."); |
| 372 } | 419 } |
| 373 } | 420 synchronized (cameraSwitchLock) { |
| 421 if (switchEventsHandler != null) { |
| 422 switchEventsHandler.onCameraSwitchError("Camera can not be started."); |
| 423 } |
| 424 switchEventsHandler = null; |
| 425 } |
| 426 } |
| 374 } | 427 } |
| 375 | 428 |
| 376 // (Re)start preview with the closest supported format to |width| x |height| @
|framerate|. | 429 // (Re)start preview with the closest supported format to |width| x |height| @
|framerate|. |
| 377 private void startPreviewOnCameraThread(int width, int height, int framerate)
{ | 430 private void startPreviewOnCameraThread() { |
| 378 checkIsOnCameraThread(); | 431 checkIsOnCameraThread(); |
| 379 if (!isCameraRunning.get() || camera == null) { | 432 if (cameraState != CameraState.RUNNING) { |
| 380 Logging.e(TAG, "startPreviewOnCameraThread: Camera is stopped"); | 433 Logging.e(TAG, "startPreviewOnCameraThread: Camera is not running"); |
| 381 return; | 434 return; |
| 382 } | 435 } |
| 383 Logging.d( | 436 Logging.d(TAG, "startPreviewOnCameraThread requested: " |
| 384 TAG, "startPreviewOnCameraThread requested: " + width + "x" + height + "
@" + framerate); | 437 + requestedWidth + "x" + requestedHeight + "@" + requestedFramerate); |
| 385 | |
| 386 requestedWidth = width; | |
| 387 requestedHeight = height; | |
| 388 requestedFramerate = framerate; | |
| 389 | 438 |
| 390 // Find closest supported format for |width| x |height| @ |framerate|. | 439 // Find closest supported format for |width| x |height| @ |framerate|. |
| 391 final android.hardware.Camera.Parameters parameters = camera.getParameters()
; | 440 final android.hardware.Camera.Parameters parameters = camera.getParameters()
; |
| 392 final List<CaptureFormat.FramerateRange> supportedFramerates = | 441 final List<CaptureFormat.FramerateRange> supportedFramerates = |
| 393 Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRan
ge()); | 442 Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRan
ge()); |
| 394 Logging.d(TAG, "Available fps ranges: " + supportedFramerates); | 443 Logging.d(TAG, "Available fps ranges: " + supportedFramerates); |
| 395 | 444 |
| 396 final CaptureFormat.FramerateRange fpsRange = | 445 final CaptureFormat.FramerateRange fpsRange = |
| 397 CameraEnumerationAndroid.getClosestSupportedFramerateRange(supportedFram
erates, framerate); | 446 CameraEnumerationAndroid.getClosestSupportedFramerateRange( |
| 447 supportedFramerates, requestedFramerate); |
| 398 | 448 |
| 399 final Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize( | 449 final Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize( |
| 400 Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()), w
idth, height); | 450 Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()), |
| 451 requestedWidth, requestedHeight); |
| 401 | 452 |
| 402 final CaptureFormat captureFormat = | 453 final CaptureFormat captureFormat = |
| 403 new CaptureFormat(previewSize.width, previewSize.height, fpsRange); | 454 new CaptureFormat(previewSize.width, previewSize.height, fpsRange); |
| 404 | 455 |
| 405 // Check if we are already using this capture format, then we don't need to
do anything. | 456 // Check if we are already using this capture format, then we don't need to
do anything. |
| 406 if (captureFormat.equals(this.captureFormat)) { | 457 if (captureFormat.equals(this.captureFormat)) { |
| 407 return; | 458 return; |
| 408 } | 459 } |
| 409 | 460 |
| 410 // Update camera parameters. | 461 // Update camera parameters. |
| 411 Logging.d(TAG, "isVideoStabilizationSupported: " + | 462 Logging.d(TAG, "isVideoStabilizationSupported: " + |
| 412 parameters.isVideoStabilizationSupported()); | 463 parameters.isVideoStabilizationSupported()); |
| 413 if (parameters.isVideoStabilizationSupported()) { | 464 if (parameters.isVideoStabilizationSupported()) { |
| 414 parameters.setVideoStabilization(true); | 465 parameters.setVideoStabilization(true); |
| 415 } | 466 } |
| 416 // Note: setRecordingHint(true) actually decrease frame rate on N5. | 467 // Note: setRecordingHint(true) actually decrease frame rate on N5. |
| 417 // parameters.setRecordingHint(true); | 468 // parameters.setRecordingHint(true); |
| 418 if (captureFormat.framerate.max > 0) { | 469 if (captureFormat.framerate.max > 0) { |
| 419 parameters.setPreviewFpsRange(captureFormat.framerate.min, captureFormat.f
ramerate.max); | 470 parameters.setPreviewFpsRange(captureFormat.framerate.min, captureFormat.f
ramerate.max); |
| 420 } | 471 } |
| 421 parameters.setPreviewSize(previewSize.width, previewSize.height); | 472 parameters.setPreviewSize(previewSize.width, previewSize.height); |
| 422 | 473 |
| 423 if (!isCapturingToTexture) { | 474 if (!isCapturingToTexture) { |
| 424 parameters.setPreviewFormat(captureFormat.imageFormat); | 475 parameters.setPreviewFormat(captureFormat.imageFormat); |
| 425 } | 476 } |
| 426 // Picture size is for taking pictures and not for preview/video, but we nee
d to set it anyway | 477 // Picture size is for taking pictures and not for preview/video, but we nee
d to set it anyway |
| 427 // as a workaround for an aspect ratio problem on Nexus 7. | 478 // as a workaround for an aspect ratio problem on Nexus 7. |
| 428 final Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize( | 479 final Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize( |
| 429 Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), w
idth, height); | 480 Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), |
| 481 requestedWidth, requestedHeight); |
| 430 parameters.setPictureSize(pictureSize.width, pictureSize.height); | 482 parameters.setPictureSize(pictureSize.width, pictureSize.height); |
| 431 | 483 |
| 432 // Temporarily stop preview if it's already running. | 484 // Temporarily stop preview if it's already running. |
| 433 if (this.captureFormat != null) { | 485 if (this.captureFormat != null) { |
| 434 camera.stopPreview(); | 486 camera.stopPreview(); |
| 435 // Calling |setPreviewCallbackWithBuffer| with null should clear the inter
nal camera buffer | 487 // Calling |setPreviewCallbackWithBuffer| with null should clear the inter
nal camera buffer |
| 436 // queue, but sometimes we receive a frame with the old resolution after t
his call anyway. | 488 // queue, but sometimes we receive a frame with the old resolution after t
his call anyway. |
| 437 camera.setPreviewCallbackWithBuffer(null); | 489 camera.setPreviewCallbackWithBuffer(null); |
| 438 } | 490 } |
| 439 | 491 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 455 for (int i = 0; i < NUMBER_OF_CAPTURE_BUFFERS; ++i) { | 507 for (int i = 0; i < NUMBER_OF_CAPTURE_BUFFERS; ++i) { |
| 456 final ByteBuffer buffer = ByteBuffer.allocateDirect(frameSize); | 508 final ByteBuffer buffer = ByteBuffer.allocateDirect(frameSize); |
| 457 queuedBuffers.add(buffer.array()); | 509 queuedBuffers.add(buffer.array()); |
| 458 camera.addCallbackBuffer(buffer.array()); | 510 camera.addCallbackBuffer(buffer.array()); |
| 459 } | 511 } |
| 460 camera.setPreviewCallbackWithBuffer(this); | 512 camera.setPreviewCallbackWithBuffer(this); |
| 461 } | 513 } |
| 462 camera.startPreview(); | 514 camera.startPreview(); |
| 463 } | 515 } |
| 464 | 516 |
| 465 // Blocks until camera is known to be stopped. | 517 // Blocks until camera is known to be stopped. Synchronized to allow only on s
top capture call at |
| 518 // a time. |
| 466 @Override | 519 @Override |
| 467 public void stopCapture() throws InterruptedException { | 520 public synchronized void stopCapture() { |
| 521 if (!checkIsInitialized()) { |
| 522 return; |
| 523 } |
| 468 Logging.d(TAG, "stopCapture"); | 524 Logging.d(TAG, "stopCapture"); |
| 469 final CountDownLatch barrier = new CountDownLatch(1); | 525 |
| 470 final boolean didPost = maybePostOnCameraThread(new Runnable() { | 526 // Count down latch to ensure runnable has been started and it is not some o
ther callback |
| 527 // stopping the camera. |
| 528 final CountDownLatch countDownLatch = new CountDownLatch(1); |
| 529 |
| 530 postOnCameraThread(new Runnable() { |
| 471 @Override public void run() { | 531 @Override public void run() { |
| 472 stopCaptureOnCameraThread(true /* stopHandler */); | 532 countDownLatch.countDown(); |
| 473 barrier.countDown(); | 533 stopCaptureOnCameraThread(); |
| 474 } | 534 } |
| 475 }); | 535 }); |
| 476 if (!didPost) { | 536 |
| 477 Logging.e(TAG, "Calling stopCapture() for already stopped camera."); | 537 Logging.d(TAG, "stopCapture: Waiting for the stopping to start"); |
| 478 return; | 538 ThreadUtils.awaitUninterruptibly(countDownLatch); |
| 479 } | 539 Logging.d(TAG, "stopCapture: Waiting for the camera to stop"); |
| 480 if (!barrier.await(CAMERA_STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { | 540 synchronized (cameraStateLock) { |
| 481 Logging.e(TAG, "Camera stop timeout"); | 541 while (cameraState != CameraState.IDLE) { |
| 482 printStackTrace(); | 542 try { |
| 483 if (eventsHandler != null) { | 543 cameraStateLock.wait(); |
| 484 eventsHandler.onCameraError("Camera stop timeout"); | 544 } catch (InterruptedException e) { |
| 545 Logging.w(TAG, "Interrupt while waiting capturer to stop: " |
| 546 + e.getMessage()); |
| 547 } |
| 485 } | 548 } |
| 486 } | 549 } |
| 487 frameObserver.onCapturerStopped(); | 550 |
| 488 Logging.d(TAG, "stopCapture done"); | 551 Logging.d(TAG, "stopCapture done"); |
| 489 } | 552 } |
| 490 | 553 |
| 491 private void stopCaptureOnCameraThread(boolean stopHandler) { | 554 private void stopCaptureOnCameraThread() { |
| 492 checkIsOnCameraThread(); | 555 checkIsOnCameraThread(); |
| 493 Logging.d(TAG, "stopCaptureOnCameraThread"); | 556 Logging.d(TAG, "stopCaptureOnCameraThread"); |
| 494 // Note that the camera might still not be started here if startCaptureOnCam
eraThread failed | 557 |
| 495 // and we posted a retry. | 558 if (cameraState == CameraState.IDLE) { |
| 559 Logging.d(TAG, "Calling stopCapture() for already stopped camera."); |
| 560 return; |
| 561 } |
| 562 |
| 563 final CameraState oldState = cameraState; |
| 564 |
| 565 // Clear the cameraThreadHandler first, in case stopPreview or |
| 566 // other driver code deadlocks. Deadlock in |
| 567 // android.hardware.Camera._stopPreview(Native Method) has |
| 568 // been observed on Nexus 5 (hammerhead), OS version LMY48I. |
| 569 // The camera might post another one or two preview frames |
| 570 // before stopped, so we have to check |isCameraRunning|. |
| 571 // Remove all pending Runnables posted from |this|. |
| 572 // Make sure no other callback starts the camera again after this |
| 573 cameraThreadHandler.removeCallbacksAndMessages(this /* token */); |
| 574 setCameraState(CameraState.IDLE); // No more frames will be delivered |
| 575 |
| 576 if (oldState == CameraState.STARTING) { |
| 577 Logging.d(TAG, "Camera starting while calling stopCapture, canceling."); |
| 578 capturerObserver.onCapturerStarted(false); |
| 579 setCameraState(CameraState.IDLE); |
| 580 return; |
| 581 } |
| 582 |
| 583 capturerObserver.onCapturerStopped(); |
| 584 |
| 585 releaseCameraOnCameraThread(); |
| 586 if (eventsHandler != null) { |
| 587 eventsHandler.onCameraClosed(); |
| 588 } |
| 589 |
| 590 Logging.d(TAG, "stopCaptureOnCameraThread done"); |
| 591 } |
| 592 |
| 593 // Note: stopPreview or other driver code might deadlock. Deadlock in |
| 594 // android.hardware.Camera._stopPreview(Native Method) has |
| 595 // been observed on Nexus 5 (hammerhead), OS version LMY48I. |
| 596 private void releaseCameraOnCameraThread() { |
| 597 checkIsOnCameraThread(); |
| 496 | 598 |
| 497 // Make sure onTextureFrameAvailable() is not called anymore. | 599 // Make sure onTextureFrameAvailable() is not called anymore. |
| 498 if (surfaceHelper != null) { | 600 surfaceHelper.stopListening(); |
| 499 surfaceHelper.stopListening(); | |
| 500 } | |
| 501 if (stopHandler) { | |
| 502 // Clear the cameraThreadHandler first, in case stopPreview or | |
| 503 // other driver code deadlocks. Deadlock in | |
| 504 // android.hardware.Camera._stopPreview(Native Method) has | |
| 505 // been observed on Nexus 5 (hammerhead), OS version LMY48I. | |
| 506 // The camera might post another one or two preview frames | |
| 507 // before stopped, so we have to check |isCameraRunning|. | |
| 508 // Remove all pending Runnables posted from |this|. | |
| 509 isCameraRunning.set(false); | |
| 510 cameraThreadHandler.removeCallbacksAndMessages(this /* token */); | |
| 511 } | |
| 512 if (cameraStatistics != null) { | 601 if (cameraStatistics != null) { |
| 513 cameraStatistics.release(); | 602 cameraStatistics.release(); |
| 514 cameraStatistics = null; | 603 cameraStatistics = null; |
| 515 } | 604 } |
| 516 Logging.d(TAG, "Stop preview."); | 605 Logging.d(TAG, "Stop preview."); |
| 517 if (camera != null) { | 606 if (camera != null) { |
| 518 camera.stopPreview(); | 607 camera.stopPreview(); |
| 519 camera.setPreviewCallbackWithBuffer(null); | 608 camera.setPreviewCallbackWithBuffer(null); |
| 520 } | 609 } |
| 521 queuedBuffers.clear(); | 610 queuedBuffers.clear(); |
| 522 captureFormat = null; | 611 captureFormat = null; |
| 523 | |
| 524 Logging.d(TAG, "Release camera."); | 612 Logging.d(TAG, "Release camera."); |
| 525 if (camera != null) { | 613 if (camera != null) { |
| 526 camera.release(); | 614 camera.release(); |
| 527 camera = null; | 615 camera = null; |
| 528 } | 616 } |
| 529 if (eventsHandler != null) { | |
| 530 eventsHandler.onCameraClosed(); | |
| 531 } | |
| 532 Logging.d(TAG, "stopCaptureOnCameraThread done"); | |
| 533 } | 617 } |
| 534 | 618 |
| 535 private void switchCameraOnCameraThread() { | 619 private void switchCameraOnCameraThread() { |
| 536 checkIsOnCameraThread(); | 620 checkIsOnCameraThread(); |
| 537 if (!isCameraRunning.get()) { | 621 Logging.d(TAG, "switchCameraOnCameraThread"); |
| 622 |
| 623 if (cameraState == CameraState.IDLE) { |
| 538 Logging.e(TAG, "switchCameraOnCameraThread: Camera is stopped"); | 624 Logging.e(TAG, "switchCameraOnCameraThread: Camera is stopped"); |
| 625 if (switchEventsHandler != null) { |
| 626 switchEventsHandler.onCameraSwitchError("Camera is stopped."); |
| 627 } |
| 539 return; | 628 return; |
| 540 } | 629 } |
| 541 Logging.d(TAG, "switchCameraOnCameraThread"); | 630 |
| 542 stopCaptureOnCameraThread(false /* stopHandler */); | 631 stopCaptureOnCameraThread(); |
| 543 synchronized (cameraIdLock) { | 632 synchronized (cameraIdLock) { |
| 544 id = (id + 1) % android.hardware.Camera.getNumberOfCameras(); | 633 id = (id + 1) % android.hardware.Camera.getNumberOfCameras(); |
| 545 } | 634 } |
| 546 startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramera
te); | 635 startCaptureOnCameraThread(requestedWidth, requestedHeight, |
| 636 requestedFramerate); |
| 637 |
| 547 Logging.d(TAG, "switchCameraOnCameraThread done"); | 638 Logging.d(TAG, "switchCameraOnCameraThread done"); |
| 548 } | 639 } |
| 549 | 640 |
| 550 private void onOutputFormatRequestOnCameraThread(int width, int height, int fr
amerate) { | 641 private void onOutputFormatRequestOnCameraThread(int width, int height, int fr
amerate) { |
| 551 checkIsOnCameraThread(); | 642 checkIsOnCameraThread(); |
| 552 Logging.d(TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + heigh
t + | 643 Logging.d(TAG, "onOutputFormatRequestOnCameraThread: " + width + "x" + heigh
t + |
| 553 "@" + framerate); | 644 "@" + framerate); |
| 554 frameObserver.onOutputFormatRequest(width, height, framerate); | 645 capturerObserver.onOutputFormatRequest(width, height, framerate); |
| 555 } | 646 } |
| 556 | 647 |
| 557 private int getDeviceOrientation() { | 648 private int getDeviceOrientation() { |
| 558 int orientation = 0; | 649 int orientation = 0; |
| 559 | 650 |
| 560 WindowManager wm = (WindowManager) applicationContext.getSystemService( | 651 WindowManager wm = (WindowManager) applicationContext.getSystemService( |
| 561 Context.WINDOW_SERVICE); | 652 Context.WINDOW_SERVICE); |
| 562 switch(wm.getDefaultDisplay().getRotation()) { | 653 switch(wm.getDefaultDisplay().getRotation()) { |
| 563 case Surface.ROTATION_90: | 654 case Surface.ROTATION_90: |
| 564 orientation = 90; | 655 orientation = 90; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 581 int rotation = getDeviceOrientation(); | 672 int rotation = getDeviceOrientation(); |
| 582 if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) { | 673 if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) { |
| 583 rotation = 360 - rotation; | 674 rotation = 360 - rotation; |
| 584 } | 675 } |
| 585 return (info.orientation + rotation) % 360; | 676 return (info.orientation + rotation) % 360; |
| 586 } | 677 } |
| 587 | 678 |
| 588 // Called on cameraThread so must not "synchronized". | 679 // Called on cameraThread so must not "synchronized". |
| 589 @Override | 680 @Override |
| 590 public void onPreviewFrame(byte[] data, android.hardware.Camera callbackCamera
) { | 681 public void onPreviewFrame(byte[] data, android.hardware.Camera callbackCamera
) { |
| 591 checkIsOnCameraThread(); | 682 if (cameraState != CameraState.RUNNING) { |
| 592 if (!isCameraRunning.get()) { | 683 Logging.d(TAG, "onPreviewFrame: Camera is stopped"); |
| 593 Logging.e(TAG, "onPreviewFrame: Camera is stopped"); | |
| 594 return; | 684 return; |
| 595 } | 685 } |
| 686 checkIsOnCameraThread(); |
| 687 |
| 596 if (!queuedBuffers.contains(data)) { | 688 if (!queuedBuffers.contains(data)) { |
| 597 // |data| is an old invalid buffer. | 689 // |data| is an old invalid buffer. |
| 598 return; | 690 return; |
| 599 } | 691 } |
| 600 if (camera != callbackCamera) { | 692 if (camera != callbackCamera) { |
| 601 throw new RuntimeException("Unexpected camera in callback!"); | 693 throw new RuntimeException("Unexpected camera in callback!"); |
| 602 } | 694 } |
| 603 | 695 |
| 604 final long captureTimeNs = | 696 final long captureTimeNs = |
| 605 TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); | 697 TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); |
| 606 | 698 |
| 607 if (eventsHandler != null && !firstFrameReported) { | 699 if (eventsHandler != null && !firstFrameReported) { |
| 608 eventsHandler.onFirstFrameAvailable(); | 700 eventsHandler.onFirstFrameAvailable(); |
| 609 firstFrameReported = true; | 701 firstFrameReported = true; |
| 610 } | 702 } |
| 611 | 703 |
| 612 cameraStatistics.addFrame(); | 704 cameraStatistics.addFrame(); |
| 613 frameObserver.onByteBufferFrameCaptured(data, captureFormat.width, captureFo
rmat.height, | 705 capturerObserver.onByteBufferFrameCaptured(data, captureFormat.width, captur
eFormat.height, |
| 614 getFrameOrientation(), captureTimeNs); | 706 getFrameOrientation(), captureTimeNs); |
| 615 camera.addCallbackBuffer(data); | 707 camera.addCallbackBuffer(data); |
| 616 } | 708 } |
| 617 | 709 |
| 618 @Override | 710 @Override |
| 619 public void onTextureFrameAvailable( | 711 public void onTextureFrameAvailable( |
| 620 int oesTextureId, float[] transformMatrix, long timestampNs) { | 712 int oesTextureId, float[] transformMatrix, long timestampNs) { |
| 621 checkIsOnCameraThread(); | 713 if (cameraState != CameraState.RUNNING) { |
| 622 if (!isCameraRunning.get()) { | 714 Logging.d(TAG, "onTextureFrameAvailable: Camera is stopped"); |
| 623 Logging.e(TAG, "onTextureFrameAvailable: Camera is stopped"); | |
| 624 surfaceHelper.returnTextureFrame(); | 715 surfaceHelper.returnTextureFrame(); |
| 625 return; | 716 return; |
| 626 } | 717 } |
| 718 checkIsOnCameraThread(); |
| 719 |
| 627 if (eventsHandler != null && !firstFrameReported) { | 720 if (eventsHandler != null && !firstFrameReported) { |
| 628 eventsHandler.onFirstFrameAvailable(); | 721 eventsHandler.onFirstFrameAvailable(); |
| 629 firstFrameReported = true; | 722 firstFrameReported = true; |
| 630 } | 723 } |
| 631 | 724 |
| 632 int rotation = getFrameOrientation(); | 725 int rotation = getFrameOrientation(); |
| 633 if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) { | 726 if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) { |
| 634 // Undo the mirror that the OS "helps" us with. | 727 // Undo the mirror that the OS "helps" us with. |
| 635 // http://developer.android.com/reference/android/hardware/Camera.html#set
DisplayOrientation(int) | 728 // http://developer.android.com/reference/android/hardware/Camera.html#set
DisplayOrientation(int) |
| 636 transformMatrix = | 729 transformMatrix = |
| 637 RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizo
ntalFlipMatrix()); | 730 RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizo
ntalFlipMatrix()); |
| 638 } | 731 } |
| 639 cameraStatistics.addFrame(); | 732 cameraStatistics.addFrame(); |
| 640 frameObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.heig
ht, oesTextureId, | 733 capturerObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.h
eight, oesTextureId, |
| 641 transformMatrix, rotation, timestampNs); | 734 transformMatrix, rotation, timestampNs); |
| 642 } | 735 } |
| 643 } | 736 } |
| OLD | NEW |