OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2016 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 |
11 package org.webrtc; | 11 package org.webrtc; |
12 | 12 |
13 import android.content.Context; | 13 import android.content.Context; |
14 import android.media.MediaRecorder; | |
14 import android.os.Handler; | 15 import android.os.Handler; |
15 import android.os.Looper; | 16 import android.os.Looper; |
16 import java.util.Arrays; | 17 import java.util.Arrays; |
17 | 18 |
18 @SuppressWarnings("deprecation") | 19 @SuppressWarnings("deprecation") |
19 abstract class CameraCapturer implements CameraVideoCapturer { | 20 abstract class CameraCapturer implements CameraVideoCapturer { |
20 enum SwitchState { | 21 enum SwitchState { |
21 IDLE, // No switch requested. | 22 IDLE, // No switch requested. |
22 PENDING, // Waiting for previous capture session to open. | 23 PENDING, // Waiting for previous capture session to open. |
23 IN_PROGRESS, // Waiting for new switched capture session to start. | 24 IN_PROGRESS, // Waiting for new switched capture session to start. |
24 } | 25 } |
25 | 26 |
27 enum MediaRecorderState { | |
28 IDLE, // No media recording update (add or remove) requested. | |
29 IN_PROGRESS, // Waiting for new capture session with updated MediaRecorder s urface to start. | |
30 } | |
31 | |
26 private static final String TAG = "CameraCapturer"; | 32 private static final String TAG = "CameraCapturer"; |
27 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; | 33 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; |
28 private final static int OPEN_CAMERA_DELAY_MS = 500; | 34 private final static int OPEN_CAMERA_DELAY_MS = 500; |
29 private final static int OPEN_CAMERA_TIMEOUT = 10000; | 35 private final static int OPEN_CAMERA_TIMEOUT = 10000; |
30 | 36 |
31 private final CameraEnumerator cameraEnumerator; | 37 private final CameraEnumerator cameraEnumerator; |
32 private final CameraEventsHandler eventsHandler; | 38 private final CameraEventsHandler eventsHandler; |
33 private final Handler uiThreadHandler; | 39 private final Handler uiThreadHandler; |
34 | 40 |
35 private final CameraSession.CreateSessionCallback createSessionCallback = | 41 private final CameraSession.CreateSessionCallback createSessionCallback = |
(...skipping 13 matching lines...) Expand all Loading... | |
49 | 55 |
50 if (switchState == SwitchState.IN_PROGRESS) { | 56 if (switchState == SwitchState.IN_PROGRESS) { |
51 if (switchEventsHandler != null) { | 57 if (switchEventsHandler != null) { |
52 switchEventsHandler.onCameraSwitchDone(cameraEnumerator.isFrontF acing(cameraName)); | 58 switchEventsHandler.onCameraSwitchDone(cameraEnumerator.isFrontF acing(cameraName)); |
53 switchEventsHandler = null; | 59 switchEventsHandler = null; |
54 } | 60 } |
55 switchState = SwitchState.IDLE; | 61 switchState = SwitchState.IDLE; |
56 } else if (switchState == SwitchState.PENDING) { | 62 } else if (switchState == SwitchState.PENDING) { |
57 switchState = SwitchState.IDLE; | 63 switchState = SwitchState.IDLE; |
58 switchCameraInternal(switchEventsHandler); | 64 switchCameraInternal(switchEventsHandler); |
65 } else if (mediaRecorderState == MediaRecorderState.IN_PROGRESS) { | |
sakal
2017/04/21 08:06:15
One more thing, this shouldn't be else if. switchS
AlexG
2017/04/21 19:38:11
Done.
| |
66 if (mediaRecorderEventsHandler != null) { | |
67 mediaRecorderEventsHandler.onMediaRecorderSuccess(); | |
68 mediaRecorderEventsHandler = null; | |
69 } | |
70 mediaRecorder = null; | |
sakal
2017/04/21 08:06:15
Why is mediaRecorder set to null here? This means
AlexG
2017/04/21 19:38:11
Yes, camera switch is not allowed while MediaRecor
| |
71 mediaRecorderState = MediaRecorderState.IDLE; | |
59 } | 72 } |
60 } | 73 } |
61 } | 74 } |
62 | 75 |
63 @Override | 76 @Override |
64 public void onFailure(CameraSession.FailureType failureType, String erro r) { | 77 public void onFailure(CameraSession.FailureType failureType, String erro r) { |
65 checkIsOnCameraThread(); | 78 checkIsOnCameraThread(); |
66 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); | 79 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); |
67 synchronized (stateLock) { | 80 synchronized (stateLock) { |
68 capturerObserver.onCapturerStarted(false /* success */); | 81 capturerObserver.onCapturerStarted(false /* success */); |
69 openAttemptsRemaining--; | 82 openAttemptsRemaining--; |
70 | 83 |
71 if (openAttemptsRemaining <= 0) { | 84 if (openAttemptsRemaining <= 0) { |
72 Logging.w(TAG, "Opening camera failed, passing: " + error); | 85 Logging.w(TAG, "Opening camera failed, passing: " + error); |
73 sessionOpening = false; | 86 sessionOpening = false; |
74 stateLock.notifyAll(); | 87 stateLock.notifyAll(); |
75 | 88 |
76 if (switchState != SwitchState.IDLE) { | 89 if (switchState != SwitchState.IDLE) { |
77 if (switchEventsHandler != null) { | 90 if (switchEventsHandler != null) { |
78 switchEventsHandler.onCameraSwitchError(error); | 91 switchEventsHandler.onCameraSwitchError(error); |
79 switchEventsHandler = null; | 92 switchEventsHandler = null; |
80 } | 93 } |
81 switchState = SwitchState.IDLE; | 94 switchState = SwitchState.IDLE; |
82 } | 95 } |
96 if (mediaRecorderState == MediaRecorderState.IN_PROGRESS) { | |
97 if (mediaRecorderEventsHandler != null) { | |
98 mediaRecorderEventsHandler.onMediaRecorderError(error); | |
99 mediaRecorderEventsHandler = null; | |
100 } | |
101 mediaRecorder = null; | |
102 mediaRecorderState = MediaRecorderState.IDLE; | |
103 } | |
83 | 104 |
84 if (failureType == CameraSession.FailureType.DISCONNECTED) { | 105 if (failureType == CameraSession.FailureType.DISCONNECTED) { |
85 eventsHandler.onCameraDisconnected(); | 106 eventsHandler.onCameraDisconnected(); |
86 } else { | 107 } else { |
87 eventsHandler.onCameraError(error); | 108 eventsHandler.onCameraError(error); |
88 } | 109 } |
89 } else { | 110 } else { |
90 Logging.w(TAG, "Opening camera failed, retry: " + error); | 111 Logging.w(TAG, "Opening camera failed, retry: " + error); |
91 | 112 |
92 createSessionInternal(OPEN_CAMERA_DELAY_MS); | 113 createSessionInternal(OPEN_CAMERA_DELAY_MS); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
191 eventsHandler.onCameraError("Camera failed to start within timeout."); | 212 eventsHandler.onCameraError("Camera failed to start within timeout."); |
192 } | 213 } |
193 }; | 214 }; |
194 | 215 |
195 // Initialized on initialize | 216 // Initialized on initialize |
196 // ------------------------- | 217 // ------------------------- |
197 private Handler cameraThreadHandler; | 218 private Handler cameraThreadHandler; |
198 private Context applicationContext; | 219 private Context applicationContext; |
199 private CapturerObserver capturerObserver; | 220 private CapturerObserver capturerObserver; |
200 private SurfaceTextureHelper surfaceHelper; | 221 private SurfaceTextureHelper surfaceHelper; |
222 private MediaRecorder mediaRecorder; | |
201 | 223 |
202 private final Object stateLock = new Object(); | 224 private final Object stateLock = new Object(); |
203 private boolean sessionOpening; /* guarded by stateLock */ | 225 private boolean sessionOpening; /* guarded by stateLock */ |
204 private CameraSession currentSession; /* guarded by stateLock */ | 226 private CameraSession currentSession; /* guarded by stateLock */ |
205 private String cameraName; /* guarded by stateLock */ | 227 private String cameraName; /* guarded by stateLock */ |
206 private int width; /* guarded by stateLock */ | 228 private int width; /* guarded by stateLock */ |
207 private int height; /* guarded by stateLock */ | 229 private int height; /* guarded by stateLock */ |
208 private int framerate; /* guarded by stateLock */ | 230 private int framerate; /* guarded by stateLock */ |
209 private int openAttemptsRemaining; /* guarded by stateLock */ | 231 private int openAttemptsRemaining; /* guarded by stateLock */ |
210 private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */ | 232 private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */ |
233 /* guarded by stateLock */ | |
234 private MediaRecorderState mediaRecorderState = MediaRecorderState.IDLE; | |
sakal
2017/04/21 07:56:49
Since these are only manipulated on the camera thr
AlexG
2017/04/21 19:38:11
Done.
| |
211 private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */ | 235 private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */ |
236 private MediaRecorderHandler mediaRecorderEventsHandler; /* guarded by stateLo ck */ | |
212 // Valid from onDone call until stopCapture, otherwise null. | 237 // Valid from onDone call until stopCapture, otherwise null. |
213 private CameraStatistics cameraStatistics; /* guarded by stateLock */ | 238 private CameraStatistics cameraStatistics; /* guarded by stateLock */ |
214 private boolean firstFrameObserved; /* guarded by stateLock */ | 239 private boolean firstFrameObserved; /* guarded by stateLock */ |
215 | 240 |
216 public CameraCapturer( | 241 public CameraCapturer( |
217 String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cam eraEnumerator) { | 242 String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cam eraEnumerator) { |
218 if (eventsHandler == null) { | 243 if (eventsHandler == null) { |
219 eventsHandler = new CameraEventsHandler() { | 244 eventsHandler = new CameraEventsHandler() { |
220 @Override | 245 @Override |
221 public void onCameraError(String errorDescription) {} | 246 public void onCameraError(String errorDescription) {} |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
280 createSessionInternal(0); | 305 createSessionInternal(0); |
281 } | 306 } |
282 } | 307 } |
283 | 308 |
284 private void createSessionInternal(int delayMs) { | 309 private void createSessionInternal(int delayMs) { |
285 uiThreadHandler.postDelayed(openCameraTimeoutRunnable, delayMs + OPEN_CAMERA _TIMEOUT); | 310 uiThreadHandler.postDelayed(openCameraTimeoutRunnable, delayMs + OPEN_CAMERA _TIMEOUT); |
286 cameraThreadHandler.postDelayed(new Runnable() { | 311 cameraThreadHandler.postDelayed(new Runnable() { |
287 @Override | 312 @Override |
288 public void run() { | 313 public void run() { |
289 createCameraSession(createSessionCallback, cameraSessionEventsHandler, a pplicationContext, | 314 createCameraSession(createSessionCallback, cameraSessionEventsHandler, a pplicationContext, |
290 surfaceHelper, cameraName, width, height, framerate); | 315 surfaceHelper, mediaRecorder, cameraName, width, height, framerate); |
291 } | 316 } |
292 }, delayMs); | 317 }, delayMs); |
293 } | 318 } |
294 | 319 |
295 @Override | 320 @Override |
296 public void stopCapture() { | 321 public void stopCapture() { |
297 Logging.d(TAG, "Stop capture"); | 322 Logging.d(TAG, "Stop capture"); |
298 | 323 |
299 synchronized (stateLock) { | 324 synchronized (stateLock) { |
300 while (sessionOpening) { | 325 while (sessionOpening) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
343 Logging.d(TAG, "switchCamera"); | 368 Logging.d(TAG, "switchCamera"); |
344 cameraThreadHandler.post(new Runnable() { | 369 cameraThreadHandler.post(new Runnable() { |
345 @Override | 370 @Override |
346 public void run() { | 371 public void run() { |
347 switchCameraInternal(switchEventsHandler); | 372 switchCameraInternal(switchEventsHandler); |
348 } | 373 } |
349 }); | 374 }); |
350 } | 375 } |
351 | 376 |
352 @Override | 377 @Override |
378 public void addMediaRecorderToCamera( | |
379 final MediaRecorder mediaRecorder, final MediaRecorderHandler mediaRecoder EventsHandler) { | |
380 Logging.d(TAG, "addMediaRecorderToCamera"); | |
381 cameraThreadHandler.post(new Runnable() { | |
382 @Override | |
383 public void run() { | |
384 updateMediaRecorderInternal(mediaRecorder, mediaRecoderEventsHandler); | |
385 } | |
386 }); | |
387 } | |
388 | |
389 @Override | |
390 public void removeMediaRecorderFromCamera(final MediaRecorderHandler mediaReco derEventsHandler) { | |
391 Logging.d(TAG, "removeMediaRecorderFromCamera"); | |
392 cameraThreadHandler.post(new Runnable() { | |
393 @Override | |
394 public void run() { | |
395 updateMediaRecorderInternal(null /* mediaRecorder */, mediaRecoderEvents Handler); | |
396 } | |
397 }); | |
398 } | |
399 | |
400 @Override | |
353 public boolean isScreencast() { | 401 public boolean isScreencast() { |
354 return false; | 402 return false; |
355 } | 403 } |
356 | 404 |
357 public void printStackTrace() { | 405 public void printStackTrace() { |
358 Thread cameraThread = null; | 406 Thread cameraThread = null; |
359 if (cameraThreadHandler != null) { | 407 if (cameraThreadHandler != null) { |
360 cameraThread = cameraThreadHandler.getLooper().getThread(); | 408 cameraThread = cameraThreadHandler.getLooper().getThread(); |
361 } | 409 } |
362 if (cameraThread != null) { | 410 if (cameraThread != null) { |
(...skipping 30 matching lines...) Expand all Loading... | |
393 | 441 |
394 if (!sessionOpening && currentSession == null) { | 442 if (!sessionOpening && currentSession == null) { |
395 Logging.d(TAG, "switchCamera: No session open"); | 443 Logging.d(TAG, "switchCamera: No session open"); |
396 if (switchEventsHandler != null) { | 444 if (switchEventsHandler != null) { |
397 switchEventsHandler.onCameraSwitchError("Camera is not running."); | 445 switchEventsHandler.onCameraSwitchError("Camera is not running."); |
398 } | 446 } |
399 return; | 447 return; |
400 } | 448 } |
401 | 449 |
402 this.switchEventsHandler = switchEventsHandler; | 450 this.switchEventsHandler = switchEventsHandler; |
403 if (sessionOpening) { | 451 if (sessionOpening) { |
magjed_webrtc
2017/04/21 10:45:16
We should decide if it's ok or not to call switchC
AlexG
2017/04/21 19:38:11
I think this is not allowed - we can not reconfigu
| |
404 switchState = SwitchState.PENDING; | 452 switchState = SwitchState.PENDING; |
405 return; | 453 return; |
406 } else { | 454 } else { |
407 switchState = SwitchState.IN_PROGRESS; | 455 switchState = SwitchState.IN_PROGRESS; |
408 } | 456 } |
409 | 457 |
410 Logging.d(TAG, "switchCamera: Stopping session"); | 458 Logging.d(TAG, "switchCamera: Stopping session"); |
411 cameraStatistics.release(); | 459 cameraStatistics.release(); |
412 cameraStatistics = null; | 460 cameraStatistics = null; |
413 final CameraSession oldSession = currentSession; | 461 final CameraSession oldSession = currentSession; |
414 cameraThreadHandler.post(new Runnable() { | 462 cameraThreadHandler.post(new Runnable() { |
415 @Override | 463 @Override |
416 public void run() { | 464 public void run() { |
417 oldSession.stop(); | 465 oldSession.stop(); |
418 } | 466 } |
419 }); | 467 }); |
420 currentSession = null; | 468 currentSession = null; |
421 | 469 |
422 int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName); | 470 int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName); |
423 cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length]; | 471 cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length]; |
424 | 472 |
425 sessionOpening = true; | 473 sessionOpening = true; |
426 openAttemptsRemaining = 1; | 474 openAttemptsRemaining = 1; |
427 createSessionInternal(0); | 475 createSessionInternal(0); |
428 } | 476 } |
429 Logging.d(TAG, "switchCamera done"); | 477 Logging.d(TAG, "switchCamera done"); |
430 } | 478 } |
431 | 479 |
480 private void reportUpdateMediaRecorderError( | |
481 String error, MediaRecorderHandler mediaRecoderEventsHandler) { | |
482 Logging.e(TAG, error); | |
sakal
2017/04/21 07:56:49
Please add checkIsOnCameraThread()
AlexG
2017/04/21 19:38:11
Done.
| |
483 if (mediaRecoderEventsHandler != null) { | |
484 mediaRecoderEventsHandler.onMediaRecorderError(error); | |
485 } | |
486 } | |
487 | |
488 private void updateMediaRecorderInternal( | |
489 final MediaRecorder mediaRecorder, final MediaRecorderHandler mediaRecoder EventsHandler) { | |
490 Logging.d(TAG, | |
sakal
2017/04/21 07:56:49
Please add checkIsOnCameraThread()
AlexG
2017/04/21 19:38:11
Done.
| |
491 "updateMediaRecoderInternal internal. State: " + mediaRecorderState | |
492 + ". Session opening: " + sessionOpening); | |
493 | |
494 synchronized (stateLock) { | |
495 if (mediaRecorderState != MediaRecorderState.IDLE) { | |
496 reportUpdateMediaRecorderError( | |
497 "MediaRecorder update already in progress.", mediaRecoderEventsHandl er); | |
498 return; | |
499 } | |
500 if (switchState != SwitchState.IDLE) { | |
501 reportUpdateMediaRecorderError( | |
502 "MediaRecorder update while camera is switching.", mediaRecoderEvent sHandler); | |
503 return; | |
504 } | |
505 if (sessionOpening) { | |
506 reportUpdateMediaRecorderError( | |
507 "MediaRecorder update while camera is still opening.", mediaRecoderE ventsHandler); | |
508 return; | |
509 } | |
510 | |
511 this.mediaRecorder = mediaRecorder; | |
512 this.mediaRecorderEventsHandler = mediaRecoderEventsHandler; | |
513 mediaRecorderState = MediaRecorderState.IN_PROGRESS; | |
514 | |
515 Logging.d(TAG, "updateMediaRecoder: Stopping session"); | |
516 cameraStatistics.release(); | |
517 cameraStatistics = null; | |
518 final CameraSession oldSession = currentSession; | |
519 cameraThreadHandler.post(new Runnable() { | |
520 @Override | |
521 public void run() { | |
522 oldSession.stop(); | |
523 } | |
524 }); | |
525 currentSession = null; | |
526 | |
527 sessionOpening = true; | |
528 openAttemptsRemaining = 1; | |
529 createSessionInternal(0); | |
530 } | |
531 Logging.d(TAG, "updateMediaRecoderInternal done"); | |
532 } | |
533 | |
432 private void checkIsOnCameraThread() { | 534 private void checkIsOnCameraThread() { |
433 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { | 535 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { |
434 Logging.e(TAG, "Check is on camera thread failed."); | 536 Logging.e(TAG, "Check is on camera thread failed."); |
435 throw new RuntimeException("Not on camera thread."); | 537 throw new RuntimeException("Not on camera thread."); |
436 } | 538 } |
437 } | 539 } |
438 | 540 |
439 protected String getCameraName() { | 541 protected String getCameraName() { |
440 synchronized (stateLock) { | 542 synchronized (stateLock) { |
441 return cameraName; | 543 return cameraName; |
442 } | 544 } |
443 } | 545 } |
444 | 546 |
445 abstract protected void createCameraSession( | 547 abstract protected void createCameraSession( |
446 CameraSession.CreateSessionCallback createSessionCallback, CameraSession.E vents events, | 548 CameraSession.CreateSessionCallback createSessionCallback, CameraSession.E vents events, |
447 Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, Str ing cameraName, | 549 Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, |
448 int width, int height, int framerate); | 550 MediaRecorder mediaRecoder, String cameraName, int width, int height, int framerate); |
449 } | 551 } |
OLD | NEW |