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 |