| 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 20 matching lines...) Expand all Loading... |
| 31 // An instance of this class can be created by an application using | 31 // An instance of this class can be created by an application using |
| 32 // VideoCapturerAndroid.create(); | 32 // VideoCapturerAndroid.create(); |
| 33 // This class extends VideoCapturer with a method to easily switch between the | 33 // This class extends VideoCapturer with a method to easily switch between the |
| 34 // front and back camera. It also provides methods for enumerating valid device | 34 // front and back camera. It also provides methods for enumerating valid device |
| 35 // names. | 35 // names. |
| 36 // | 36 // |
| 37 // Threading notes: this class is called from C++ code, Android Camera callbacks
, and possibly | 37 // Threading notes: this class is called from C++ code, Android Camera callbacks
, and possibly |
| 38 // arbitrary Java threads. All public entry points are thread safe, and delegate
the work to the | 38 // arbitrary Java threads. All public entry points are thread safe, and delegate
the work to the |
| 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. | |
| 42 @SuppressWarnings("deprecation") | 41 @SuppressWarnings("deprecation") |
| 43 public class VideoCapturerAndroid implements | 42 public class VideoCapturerAndroid implements |
| 44 CameraVideoCapturer, | 43 VideoCapturer, |
| 45 android.hardware.Camera.PreviewCallback, | 44 android.hardware.Camera.PreviewCallback, |
| 46 SurfaceTextureHelper.OnTextureFrameAvailableListener { | 45 SurfaceTextureHelper.OnTextureFrameAvailableListener { |
| 47 private final static String TAG = "VideoCapturerAndroid"; | 46 private final static String TAG = "VideoCapturerAndroid"; |
| 47 private final static int CAMERA_OBSERVER_PERIOD_MS = 2000; |
| 48 private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 4000; |
| 48 private static final int CAMERA_STOP_TIMEOUT_MS = 7000; | 49 private static final int CAMERA_STOP_TIMEOUT_MS = 7000; |
| 49 | 50 |
| 50 private boolean isDisposed = false; | 51 private boolean isDisposed = false; |
| 51 private android.hardware.Camera camera; // Only non-null while capturing. | 52 private android.hardware.Camera camera; // Only non-null while capturing. |
| 52 private final Object handlerLock = new Object(); | 53 private final Object handlerLock = new Object(); |
| 53 // |cameraThreadHandler| must be synchronized on |handlerLock| when not on the
camera thread, | 54 // |cameraThreadHandler| must be synchronized on |handlerLock| when not on the
camera thread, |
| 54 // or when modifying the reference. Use maybePostOnCameraThread() instead of p
osting directly to | 55 // or when modifying the reference. Use maybePostOnCameraThread() instead of p
osting directly to |
| 55 // the handler - this way all callbacks with a specifed token can be removed a
t once. | 56 // the handler - this way all callbacks with a specifed token can be removed a
t once. |
| 56 private Handler cameraThreadHandler; | 57 private Handler cameraThreadHandler; |
| 57 private Context applicationContext; | 58 private Context applicationContext; |
| 58 // Synchronization lock for |id|. | 59 // Synchronization lock for |id|. |
| 59 private final Object cameraIdLock = new Object(); | 60 private final Object cameraIdLock = new Object(); |
| 60 private int id; | 61 private int id; |
| 61 private android.hardware.Camera.CameraInfo info; | 62 private android.hardware.Camera.CameraInfo info; |
| 62 private CameraStatistics cameraStatistics; | 63 private final CameraStatistics cameraStatistics; |
| 63 // Remember the requested format in case we want to switch cameras. | 64 // Remember the requested format in case we want to switch cameras. |
| 64 private int requestedWidth; | 65 private int requestedWidth; |
| 65 private int requestedHeight; | 66 private int requestedHeight; |
| 66 private int requestedFramerate; | 67 private int requestedFramerate; |
| 67 // The capture format will be the closest supported format to the requested fo
rmat. | 68 // The capture format will be the closest supported format to the requested fo
rmat. |
| 68 private CaptureFormat captureFormat; | 69 private CaptureFormat captureFormat; |
| 69 private final Object pendingCameraSwitchLock = new Object(); | 70 private final Object pendingCameraSwitchLock = new Object(); |
| 70 private volatile boolean pendingCameraSwitch; | 71 private volatile boolean pendingCameraSwitch; |
| 71 private CapturerObserver frameObserver = null; | 72 private CapturerObserver frameObserver = null; |
| 72 private final CameraEventsHandler eventsHandler; | 73 private final CameraEventsHandler eventsHandler; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 96 } else { | 97 } else { |
| 97 errorMessage = "Camera error: " + error; | 98 errorMessage = "Camera error: " + error; |
| 98 } | 99 } |
| 99 Logging.e(TAG, errorMessage); | 100 Logging.e(TAG, errorMessage); |
| 100 if (eventsHandler != null) { | 101 if (eventsHandler != null) { |
| 101 eventsHandler.onCameraError(errorMessage); | 102 eventsHandler.onCameraError(errorMessage); |
| 102 } | 103 } |
| 103 } | 104 } |
| 104 }; | 105 }; |
| 105 | 106 |
| 107 // Camera observer - monitors camera framerate. Observer is executed on camera
thread. |
| 108 private final Runnable cameraObserver = new Runnable() { |
| 109 private int freezePeriodCount; |
| 110 @Override |
| 111 public void run() { |
| 112 int cameraFramesCount = cameraStatistics.getAndResetFrameCount(); |
| 113 int cameraFps = (cameraFramesCount * 1000 + CAMERA_OBSERVER_PERIOD_MS / 2) |
| 114 / CAMERA_OBSERVER_PERIOD_MS; |
| 115 |
| 116 Logging.d(TAG, "Camera fps: " + cameraFps +"."); |
| 117 if (cameraFramesCount == 0) { |
| 118 ++freezePeriodCount; |
| 119 if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount >= CAMERA_FREEZE_REPOR
T_TIMOUT_MS |
| 120 && eventsHandler != null) { |
| 121 Logging.e(TAG, "Camera freezed."); |
| 122 if (surfaceHelper.isTextureInUse()) { |
| 123 // This can only happen if we are capturing to textures. |
| 124 eventsHandler.onCameraFreezed("Camera failure. Client must return vi
deo buffers."); |
| 125 } else { |
| 126 eventsHandler.onCameraFreezed("Camera failure."); |
| 127 } |
| 128 return; |
| 129 } |
| 130 } else { |
| 131 freezePeriodCount = 0; |
| 132 } |
| 133 maybePostDelayedOnCameraThread(CAMERA_OBSERVER_PERIOD_MS, this); |
| 134 } |
| 135 }; |
| 136 |
| 137 private static class CameraStatistics { |
| 138 private int frameCount = 0; |
| 139 private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.Thre
adChecker(); |
| 140 |
| 141 CameraStatistics() { |
| 142 threadChecker.detachThread(); |
| 143 } |
| 144 |
| 145 public void addFrame() { |
| 146 threadChecker.checkIsOnValidThread(); |
| 147 ++frameCount; |
| 148 } |
| 149 |
| 150 public int getAndResetFrameCount() { |
| 151 threadChecker.checkIsOnValidThread(); |
| 152 int count = frameCount; |
| 153 frameCount = 0; |
| 154 return count; |
| 155 } |
| 156 } |
| 157 |
| 158 public static interface CameraEventsHandler { |
| 159 // Camera error handler - invoked when camera can not be opened |
| 160 // or any camera exception happens on camera thread. |
| 161 void onCameraError(String errorDescription); |
| 162 |
| 163 // Invoked when camera stops receiving frames |
| 164 void onCameraFreezed(String errorDescription); |
| 165 |
| 166 // Callback invoked when camera is opening. |
| 167 void onCameraOpening(int cameraId); |
| 168 |
| 169 // Callback invoked when first camera frame is available after camera is ope
ned. |
| 170 void onFirstFrameAvailable(); |
| 171 |
| 172 // Callback invoked when camera closed. |
| 173 void onCameraClosed(); |
| 174 } |
| 175 |
| 176 // Camera switch handler - one of these functions are invoked with the result
of switchCamera(). |
| 177 // The callback may be called on an arbitrary thread. |
| 178 public interface CameraSwitchHandler { |
| 179 // Invoked on success. |isFrontCamera| is true if the new camera is front fa
cing. |
| 180 void onCameraSwitchDone(boolean isFrontCamera); |
| 181 // Invoked on failure, e.g. camera is stopped or only one camera available. |
| 182 void onCameraSwitchError(String errorDescription); |
| 183 } |
| 184 |
| 106 public static VideoCapturerAndroid create(String name, | 185 public static VideoCapturerAndroid create(String name, |
| 107 CameraEventsHandler eventsHandler) { | 186 CameraEventsHandler eventsHandler) { |
| 108 return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTe
xture */); | 187 return VideoCapturerAndroid.create(name, eventsHandler, false /* captureToTe
xture */); |
| 109 } | 188 } |
| 110 | 189 |
| 111 public static VideoCapturerAndroid create(String name, | 190 public static VideoCapturerAndroid create(String name, |
| 112 CameraEventsHandler eventsHandler, boolean captureToTexture) { | 191 CameraEventsHandler eventsHandler, boolean captureToTexture) { |
| 113 final int cameraId = lookupDeviceName(name); | 192 final int cameraId = lookupDeviceName(name); |
| 114 if (cameraId == -1) { | 193 if (cameraId == -1) { |
| 115 return null; | 194 return null; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 130 Logging.d(TAG, "VideoCapturerAndroid stacks trace:"); | 209 Logging.d(TAG, "VideoCapturerAndroid stacks trace:"); |
| 131 for (StackTraceElement stackTrace : cameraStackTraces) { | 210 for (StackTraceElement stackTrace : cameraStackTraces) { |
| 132 Logging.d(TAG, stackTrace.toString()); | 211 Logging.d(TAG, stackTrace.toString()); |
| 133 } | 212 } |
| 134 } | 213 } |
| 135 } | 214 } |
| 136 } | 215 } |
| 137 | 216 |
| 138 // Switch camera to the next valid camera id. This can only be called while | 217 // Switch camera to the next valid camera id. This can only be called while |
| 139 // the camera is running. | 218 // the camera is running. |
| 140 @Override | |
| 141 public void switchCamera(final CameraSwitchHandler switchEventsHandler) { | 219 public void switchCamera(final CameraSwitchHandler switchEventsHandler) { |
| 142 if (android.hardware.Camera.getNumberOfCameras() < 2) { | 220 if (android.hardware.Camera.getNumberOfCameras() < 2) { |
| 143 if (switchEventsHandler != null) { | 221 if (switchEventsHandler != null) { |
| 144 switchEventsHandler.onCameraSwitchError("No camera to switch to."); | 222 switchEventsHandler.onCameraSwitchError("No camera to switch to."); |
| 145 } | 223 } |
| 146 return; | 224 return; |
| 147 } | 225 } |
| 148 synchronized (pendingCameraSwitchLock) { | 226 synchronized (pendingCameraSwitchLock) { |
| 149 if (pendingCameraSwitch) { | 227 if (pendingCameraSwitch) { |
| 150 // Do not handle multiple camera switch request to avoid blocking | 228 // Do not handle multiple camera switch request to avoid blocking |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 // Returns true if this VideoCapturer is setup to capture video frames to a Su
rfaceTexture. | 292 // Returns true if this VideoCapturer is setup to capture video frames to a Su
rfaceTexture. |
| 215 public boolean isCapturingToTexture() { | 293 public boolean isCapturingToTexture() { |
| 216 return isCapturingToTexture; | 294 return isCapturingToTexture; |
| 217 } | 295 } |
| 218 | 296 |
| 219 private VideoCapturerAndroid(int cameraId, CameraEventsHandler eventsHandler, | 297 private VideoCapturerAndroid(int cameraId, CameraEventsHandler eventsHandler, |
| 220 boolean captureToTexture) { | 298 boolean captureToTexture) { |
| 221 this.id = cameraId; | 299 this.id = cameraId; |
| 222 this.eventsHandler = eventsHandler; | 300 this.eventsHandler = eventsHandler; |
| 223 isCapturingToTexture = captureToTexture; | 301 isCapturingToTexture = captureToTexture; |
| 302 cameraStatistics = new CameraStatistics(); |
| 224 Logging.d(TAG, "VideoCapturerAndroid isCapturingToTexture : " + isCapturingT
oTexture); | 303 Logging.d(TAG, "VideoCapturerAndroid isCapturingToTexture : " + isCapturingT
oTexture); |
| 225 } | 304 } |
| 226 | 305 |
| 227 private void checkIsOnCameraThread() { | 306 private void checkIsOnCameraThread() { |
| 228 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { | 307 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { |
| 229 throw new IllegalStateException("Wrong thread"); | 308 throw new IllegalStateException("Wrong thread"); |
| 230 } | 309 } |
| 231 } | 310 } |
| 232 | 311 |
| 233 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be | 312 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 Logging.d(TAG, "Camera orientation: " + info.orientation + | 453 Logging.d(TAG, "Camera orientation: " + info.orientation + |
| 375 " .Device orientation: " + getDeviceOrientation()); | 454 " .Device orientation: " + getDeviceOrientation()); |
| 376 camera.setErrorCallback(cameraErrorCallback); | 455 camera.setErrorCallback(cameraErrorCallback); |
| 377 startPreviewOnCameraThread(width, height, framerate); | 456 startPreviewOnCameraThread(width, height, framerate); |
| 378 frameObserver.onCapturerStarted(true); | 457 frameObserver.onCapturerStarted(true); |
| 379 if (isCapturingToTexture) { | 458 if (isCapturingToTexture) { |
| 380 surfaceHelper.startListening(this); | 459 surfaceHelper.startListening(this); |
| 381 } | 460 } |
| 382 | 461 |
| 383 // Start camera observer. | 462 // Start camera observer. |
| 384 cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler); | 463 maybePostDelayedOnCameraThread(CAMERA_OBSERVER_PERIOD_MS, cameraObserver); |
| 385 return; | 464 return; |
| 386 } catch (RuntimeException e) { | 465 } catch (RuntimeException e) { |
| 387 error = e; | 466 error = e; |
| 388 } | 467 } |
| 389 Logging.e(TAG, "startCapture failed", error); | 468 Logging.e(TAG, "startCapture failed", error); |
| 390 // Make sure the camera is released. | 469 // Make sure the camera is released. |
| 391 stopCaptureOnCameraThread(); | 470 stopCaptureOnCameraThread(); |
| 392 synchronized (handlerLock) { | 471 synchronized (handlerLock) { |
| 393 // Remove all pending Runnables posted from |this|. | 472 // Remove all pending Runnables posted from |this|. |
| 394 cameraThreadHandler.removeCallbacksAndMessages(this /* token */); | 473 cameraThreadHandler.removeCallbacksAndMessages(this /* token */); |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 private void stopCaptureOnCameraThread() { | 606 private void stopCaptureOnCameraThread() { |
| 528 checkIsOnCameraThread(); | 607 checkIsOnCameraThread(); |
| 529 Logging.d(TAG, "stopCaptureOnCameraThread"); | 608 Logging.d(TAG, "stopCaptureOnCameraThread"); |
| 530 // Note that the camera might still not be started here if startCaptureOnCam
eraThread failed | 609 // Note that the camera might still not be started here if startCaptureOnCam
eraThread failed |
| 531 // and we posted a retry. | 610 // and we posted a retry. |
| 532 | 611 |
| 533 // Make sure onTextureFrameAvailable() is not called anymore. | 612 // Make sure onTextureFrameAvailable() is not called anymore. |
| 534 if (surfaceHelper != null) { | 613 if (surfaceHelper != null) { |
| 535 surfaceHelper.stopListening(); | 614 surfaceHelper.stopListening(); |
| 536 } | 615 } |
| 537 if (cameraStatistics != null) { | 616 cameraThreadHandler.removeCallbacks(cameraObserver); |
| 538 cameraStatistics.release(); | 617 cameraStatistics.getAndResetFrameCount(); |
| 539 cameraStatistics = null; | |
| 540 } | |
| 541 Logging.d(TAG, "Stop preview."); | 618 Logging.d(TAG, "Stop preview."); |
| 542 if (camera != null) { | 619 if (camera != null) { |
| 543 camera.stopPreview(); | 620 camera.stopPreview(); |
| 544 camera.setPreviewCallbackWithBuffer(null); | 621 camera.setPreviewCallbackWithBuffer(null); |
| 545 } | 622 } |
| 546 queuedBuffers.clear(); | 623 queuedBuffers.clear(); |
| 547 captureFormat = null; | 624 captureFormat = null; |
| 548 | 625 |
| 549 Logging.d(TAG, "Release camera."); | 626 Logging.d(TAG, "Release camera."); |
| 550 if (camera != null) { | 627 if (camera != null) { |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 669 // Undo the mirror that the OS "helps" us with. | 746 // Undo the mirror that the OS "helps" us with. |
| 670 // http://developer.android.com/reference/android/hardware/Camera.html#set
DisplayOrientation(int) | 747 // http://developer.android.com/reference/android/hardware/Camera.html#set
DisplayOrientation(int) |
| 671 transformMatrix = | 748 transformMatrix = |
| 672 RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizo
ntalFlipMatrix()); | 749 RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.horizo
ntalFlipMatrix()); |
| 673 } | 750 } |
| 674 cameraStatistics.addFrame(); | 751 cameraStatistics.addFrame(); |
| 675 frameObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.heig
ht, oesTextureId, | 752 frameObserver.onTextureFrameCaptured(captureFormat.width, captureFormat.heig
ht, oesTextureId, |
| 676 transformMatrix, rotation, timestampNs); | 753 transformMatrix, rotation, timestampNs); |
| 677 } | 754 } |
| 678 } | 755 } |
| OLD | NEW |