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