Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(272)

Side by Side Diff: webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java

Issue 2833773003: Support adding and removing MediaRecorder to camera 2 session. (Closed)
Patch Set: Add unit test Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 IDLE_TO_ACTIVE, // Waiting for new capture session with added MediaRecorder surface to start.
30 ACTIVE_TO_IDLE, // Waiting for new capture session with removed MediaRecorde r surface to start.
31 ACTIVE, // MediaRecorder was successfully added to camera pipeline.
32 }
33
26 private static final String TAG = "CameraCapturer"; 34 private static final String TAG = "CameraCapturer";
27 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; 35 private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
28 private final static int OPEN_CAMERA_DELAY_MS = 500; 36 private final static int OPEN_CAMERA_DELAY_MS = 500;
29 private final static int OPEN_CAMERA_TIMEOUT = 10000; 37 private final static int OPEN_CAMERA_TIMEOUT = 10000;
30 38
31 private final CameraEnumerator cameraEnumerator; 39 private final CameraEnumerator cameraEnumerator;
32 private final CameraEventsHandler eventsHandler; 40 private final CameraEventsHandler eventsHandler;
33 private final Handler uiThreadHandler; 41 private final Handler uiThreadHandler;
34 42
35 private final CameraSession.CreateSessionCallback createSessionCallback = 43 private final CameraSession.CreateSessionCallback createSessionCallback =
36 new CameraSession.CreateSessionCallback() { 44 new CameraSession.CreateSessionCallback() {
37 @Override 45 @Override
38 public void onDone(CameraSession session) { 46 public void onDone(CameraSession session) {
39 checkIsOnCameraThread(); 47 checkIsOnCameraThread();
40 Logging.d(TAG, "Create session done"); 48 Logging.d(TAG,
49 "Create session done. Switch state: " + switchState
50 + ". MediaRecorder state: " + mediaRecorderState);
41 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); 51 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable);
42 synchronized (stateLock) { 52 synchronized (stateLock) {
43 capturerObserver.onCapturerStarted(true /* success */); 53 capturerObserver.onCapturerStarted(true /* success */);
44 sessionOpening = false; 54 sessionOpening = false;
45 currentSession = session; 55 currentSession = session;
46 cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler ); 56 cameraStatistics = new CameraStatistics(surfaceHelper, eventsHandler );
47 firstFrameObserved = false; 57 firstFrameObserved = false;
48 stateLock.notifyAll(); 58 stateLock.notifyAll();
49 59
50 if (switchState == SwitchState.IN_PROGRESS) { 60 if (switchState == SwitchState.IN_PROGRESS) {
51 if (switchEventsHandler != null) { 61 if (switchEventsHandler != null) {
52 switchEventsHandler.onCameraSwitchDone(cameraEnumerator.isFrontF acing(cameraName)); 62 switchEventsHandler.onCameraSwitchDone(cameraEnumerator.isFrontF acing(cameraName));
53 switchEventsHandler = null; 63 switchEventsHandler = null;
54 } 64 }
55 switchState = SwitchState.IDLE; 65 switchState = SwitchState.IDLE;
56 } else if (switchState == SwitchState.PENDING) { 66 } else if (switchState == SwitchState.PENDING) {
57 switchState = SwitchState.IDLE; 67 switchState = SwitchState.IDLE;
58 switchCameraInternal(switchEventsHandler); 68 switchCameraInternal(switchEventsHandler);
59 } 69 }
70
71 if (mediaRecorderState == MediaRecorderState.IDLE_TO_ACTIVE
72 || mediaRecorderState == MediaRecorderState.ACTIVE_TO_IDLE) {
73 if (mediaRecorderEventsHandler != null) {
74 mediaRecorderEventsHandler.onMediaRecorderSuccess();
75 mediaRecorderEventsHandler = null;
76 }
77 if (mediaRecorderState == MediaRecorderState.IDLE_TO_ACTIVE) {
78 mediaRecorderState = MediaRecorderState.ACTIVE;
79 } else {
80 mediaRecorderState = MediaRecorderState.IDLE;
81 }
82 mediaRecorder = null;
83 }
60 } 84 }
61 } 85 }
62 86
63 @Override 87 @Override
64 public void onFailure(CameraSession.FailureType failureType, String erro r) { 88 public void onFailure(CameraSession.FailureType failureType, String erro r) {
65 checkIsOnCameraThread(); 89 checkIsOnCameraThread();
66 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); 90 uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable);
67 synchronized (stateLock) { 91 synchronized (stateLock) {
68 capturerObserver.onCapturerStarted(false /* success */); 92 capturerObserver.onCapturerStarted(false /* success */);
69 openAttemptsRemaining--; 93 openAttemptsRemaining--;
70 94
71 if (openAttemptsRemaining <= 0) { 95 if (openAttemptsRemaining <= 0) {
72 Logging.w(TAG, "Opening camera failed, passing: " + error); 96 Logging.w(TAG, "Opening camera failed, passing: " + error);
73 sessionOpening = false; 97 sessionOpening = false;
74 stateLock.notifyAll(); 98 stateLock.notifyAll();
75 99
76 if (switchState != SwitchState.IDLE) { 100 if (switchState != SwitchState.IDLE) {
77 if (switchEventsHandler != null) { 101 if (switchEventsHandler != null) {
78 switchEventsHandler.onCameraSwitchError(error); 102 switchEventsHandler.onCameraSwitchError(error);
79 switchEventsHandler = null; 103 switchEventsHandler = null;
80 } 104 }
81 switchState = SwitchState.IDLE; 105 switchState = SwitchState.IDLE;
82 } 106 }
83 107
108 if (mediaRecorderState != MediaRecorderState.IDLE) {
109 if (mediaRecorderEventsHandler != null) {
110 mediaRecorderEventsHandler.onMediaRecorderError(error);
111 mediaRecorderEventsHandler = null;
112 }
113 mediaRecorderState = MediaRecorderState.IDLE;
114 mediaRecorder = null;
115 }
116
84 if (failureType == CameraSession.FailureType.DISCONNECTED) { 117 if (failureType == CameraSession.FailureType.DISCONNECTED) {
85 eventsHandler.onCameraDisconnected(); 118 eventsHandler.onCameraDisconnected();
86 } else { 119 } else {
87 eventsHandler.onCameraError(error); 120 eventsHandler.onCameraError(error);
88 } 121 }
89 } else { 122 } else {
90 Logging.w(TAG, "Opening camera failed, retry: " + error); 123 Logging.w(TAG, "Opening camera failed, retry: " + error);
91 124
92 createSessionInternal(OPEN_CAMERA_DELAY_MS); 125 createSessionInternal(OPEN_CAMERA_DELAY_MS);
93 } 126 }
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 private int width; /* guarded by stateLock */ 239 private int width; /* guarded by stateLock */
207 private int height; /* guarded by stateLock */ 240 private int height; /* guarded by stateLock */
208 private int framerate; /* guarded by stateLock */ 241 private int framerate; /* guarded by stateLock */
209 private int openAttemptsRemaining; /* guarded by stateLock */ 242 private int openAttemptsRemaining; /* guarded by stateLock */
210 private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */ 243 private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */
211 private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */ 244 private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */
212 // Valid from onDone call until stopCapture, otherwise null. 245 // Valid from onDone call until stopCapture, otherwise null.
213 private CameraStatistics cameraStatistics; /* guarded by stateLock */ 246 private CameraStatistics cameraStatistics; /* guarded by stateLock */
214 private boolean firstFrameObserved; /* guarded by stateLock */ 247 private boolean firstFrameObserved; /* guarded by stateLock */
215 248
249 // Variables used on camera thread - do not require stateLock synchronization.
250 private MediaRecorderState mediaRecorderState = MediaRecorderState.IDLE;
251 private MediaRecorderHandler mediaRecorderEventsHandler;
252 private MediaRecorder mediaRecorder;
253
216 public CameraCapturer( 254 public CameraCapturer(
217 String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cam eraEnumerator) { 255 String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cam eraEnumerator) {
218 if (eventsHandler == null) { 256 if (eventsHandler == null) {
219 eventsHandler = new CameraEventsHandler() { 257 eventsHandler = new CameraEventsHandler() {
220 @Override 258 @Override
221 public void onCameraError(String errorDescription) {} 259 public void onCameraError(String errorDescription) {}
222 @Override 260 @Override
223 public void onCameraDisconnected() {} 261 public void onCameraDisconnected() {}
224 @Override 262 @Override
225 public void onCameraFreezed(String errorDescription) {} 263 public void onCameraFreezed(String errorDescription) {}
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 createSessionInternal(0); 318 createSessionInternal(0);
281 } 319 }
282 } 320 }
283 321
284 private void createSessionInternal(int delayMs) { 322 private void createSessionInternal(int delayMs) {
285 uiThreadHandler.postDelayed(openCameraTimeoutRunnable, delayMs + OPEN_CAMERA _TIMEOUT); 323 uiThreadHandler.postDelayed(openCameraTimeoutRunnable, delayMs + OPEN_CAMERA _TIMEOUT);
286 cameraThreadHandler.postDelayed(new Runnable() { 324 cameraThreadHandler.postDelayed(new Runnable() {
287 @Override 325 @Override
288 public void run() { 326 public void run() {
289 createCameraSession(createSessionCallback, cameraSessionEventsHandler, a pplicationContext, 327 createCameraSession(createSessionCallback, cameraSessionEventsHandler, a pplicationContext,
290 surfaceHelper, cameraName, width, height, framerate); 328 surfaceHelper, mediaRecorder, cameraName, width, height, framerate);
291 } 329 }
292 }, delayMs); 330 }, delayMs);
293 } 331 }
294 332
295 @Override 333 @Override
296 public void stopCapture() { 334 public void stopCapture() {
297 Logging.d(TAG, "Stop capture"); 335 Logging.d(TAG, "Stop capture");
298 336
299 synchronized (stateLock) { 337 synchronized (stateLock) {
300 while (sessionOpening) { 338 while (sessionOpening) {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 Logging.d(TAG, "switchCamera"); 381 Logging.d(TAG, "switchCamera");
344 cameraThreadHandler.post(new Runnable() { 382 cameraThreadHandler.post(new Runnable() {
345 @Override 383 @Override
346 public void run() { 384 public void run() {
347 switchCameraInternal(switchEventsHandler); 385 switchCameraInternal(switchEventsHandler);
348 } 386 }
349 }); 387 });
350 } 388 }
351 389
352 @Override 390 @Override
391 public void addMediaRecorderToCamera(
392 final MediaRecorder mediaRecorder, final MediaRecorderHandler mediaRecoder EventsHandler) {
393 Logging.d(TAG, "addMediaRecorderToCamera");
394 cameraThreadHandler.post(new Runnable() {
395 @Override
396 public void run() {
397 updateMediaRecorderInternal(mediaRecorder, mediaRecoderEventsHandler);
398 }
399 });
400 }
401
402 @Override
403 public void removeMediaRecorderFromCamera(final MediaRecorderHandler mediaReco derEventsHandler) {
404 Logging.d(TAG, "removeMediaRecorderFromCamera");
405 cameraThreadHandler.post(new Runnable() {
406 @Override
407 public void run() {
408 updateMediaRecorderInternal(null /* mediaRecorder */, mediaRecoderEvents Handler);
409 }
410 });
411 }
412
413 @Override
353 public boolean isScreencast() { 414 public boolean isScreencast() {
354 return false; 415 return false;
355 } 416 }
356 417
357 public void printStackTrace() { 418 public void printStackTrace() {
358 Thread cameraThread = null; 419 Thread cameraThread = null;
359 if (cameraThreadHandler != null) { 420 if (cameraThreadHandler != null) {
360 cameraThread = cameraThreadHandler.getLooper().getThread(); 421 cameraThread = cameraThreadHandler.getLooper().getThread();
361 } 422 }
362 if (cameraThread != null) { 423 if (cameraThread != null) {
363 StackTraceElement[] cameraStackTrace = cameraThread.getStackTrace(); 424 StackTraceElement[] cameraStackTrace = cameraThread.getStackTrace();
364 if (cameraStackTrace.length > 0) { 425 if (cameraStackTrace.length > 0) {
365 Logging.d(TAG, "CameraCapturer stack trace:"); 426 Logging.d(TAG, "CameraCapturer stack trace:");
366 for (StackTraceElement traceElem : cameraStackTrace) { 427 for (StackTraceElement traceElem : cameraStackTrace) {
367 Logging.d(TAG, traceElem.toString()); 428 Logging.d(TAG, traceElem.toString());
368 } 429 }
369 } 430 }
370 } 431 }
371 } 432 }
372 433
434 private void reportCameraSwitchError(String error, CameraSwitchHandler switchE ventsHandler) {
435 Logging.e(TAG, error);
436 if (switchEventsHandler != null) {
437 switchEventsHandler.onCameraSwitchError(error);
438 }
439 }
440
373 private void switchCameraInternal(final CameraSwitchHandler switchEventsHandle r) { 441 private void switchCameraInternal(final CameraSwitchHandler switchEventsHandle r) {
374 Logging.d(TAG, "switchCamera internal"); 442 Logging.d(TAG, "switchCamera internal");
375 443
376 final String[] deviceNames = cameraEnumerator.getDeviceNames(); 444 final String[] deviceNames = cameraEnumerator.getDeviceNames();
377 445
378 if (deviceNames.length < 2) { 446 if (deviceNames.length < 2) {
379 if (switchEventsHandler != null) { 447 if (switchEventsHandler != null) {
380 switchEventsHandler.onCameraSwitchError("No camera to switch to."); 448 switchEventsHandler.onCameraSwitchError("No camera to switch to.");
381 } 449 }
382 return; 450 return;
383 } 451 }
384 452
385 synchronized (stateLock) { 453 synchronized (stateLock) {
386 if (switchState != SwitchState.IDLE) { 454 if (switchState != SwitchState.IDLE) {
387 Logging.d(TAG, "switchCamera switchInProgress"); 455 reportCameraSwitchError("Camera switch already in progress.", switchEven tsHandler);
388 if (switchEventsHandler != null) { 456 return;
389 switchEventsHandler.onCameraSwitchError("Camera switch already in prog ress."); 457 }
390 } 458 if (mediaRecorderState != MediaRecorderState.IDLE) {
459 reportCameraSwitchError("switchCamera: media recording is active", switc hEventsHandler);
460 return;
461 }
462 if (!sessionOpening && currentSession == null) {
463 reportCameraSwitchError("switchCamera: camera is not running.", switchEv entsHandler);
391 return; 464 return;
392 } 465 }
393 466
394 if (!sessionOpening && currentSession == null) {
395 Logging.d(TAG, "switchCamera: No session open");
396 if (switchEventsHandler != null) {
397 switchEventsHandler.onCameraSwitchError("Camera is not running.");
398 }
399 return;
400 }
401
402 this.switchEventsHandler = switchEventsHandler; 467 this.switchEventsHandler = switchEventsHandler;
403 if (sessionOpening) { 468 if (sessionOpening) {
404 switchState = SwitchState.PENDING; 469 switchState = SwitchState.PENDING;
405 return; 470 return;
406 } else { 471 } else {
407 switchState = SwitchState.IN_PROGRESS; 472 switchState = SwitchState.IN_PROGRESS;
408 } 473 }
409 474
410 Logging.d(TAG, "switchCamera: Stopping session"); 475 Logging.d(TAG, "switchCamera: Stopping session");
411 cameraStatistics.release(); 476 cameraStatistics.release();
(...skipping 10 matching lines...) Expand all
422 int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName); 487 int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName);
423 cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length]; 488 cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length];
424 489
425 sessionOpening = true; 490 sessionOpening = true;
426 openAttemptsRemaining = 1; 491 openAttemptsRemaining = 1;
427 createSessionInternal(0); 492 createSessionInternal(0);
428 } 493 }
429 Logging.d(TAG, "switchCamera done"); 494 Logging.d(TAG, "switchCamera done");
430 } 495 }
431 496
497 private void reportUpdateMediaRecorderError(
498 String error, MediaRecorderHandler mediaRecoderEventsHandler) {
499 checkIsOnCameraThread();
500 Logging.e(TAG, error);
501 if (mediaRecoderEventsHandler != null) {
502 mediaRecoderEventsHandler.onMediaRecorderError(error);
503 }
504 }
505
506 private void updateMediaRecorderInternal(
507 MediaRecorder mediaRecorder, MediaRecorderHandler mediaRecoderEventsHandle r) {
508 checkIsOnCameraThread();
509 boolean addMediaRecorder = (mediaRecorder != null);
510 Logging.d(TAG,
511 "updateMediaRecoderInternal internal. State: " + mediaRecorderState
512 + ". Switch state: " + switchState + ". Add MediaRecorder: " + addMe diaRecorder);
513
514 synchronized (stateLock) {
515 if ((addMediaRecorder && mediaRecorderState != MediaRecorderState.IDLE)
516 || (!addMediaRecorder && mediaRecorderState != MediaRecorderState.ACTI VE)) {
517 reportUpdateMediaRecorderError(
518 "Incorrect state for MediaRecorder update.", mediaRecoderEventsHandl er);
519 return;
520 }
521 if (switchState != SwitchState.IDLE) {
522 reportUpdateMediaRecorderError(
523 "MediaRecorder update while camera is switching.", mediaRecoderEvent sHandler);
524 return;
525 }
526 if (currentSession == null) {
527 reportUpdateMediaRecorderError(
528 "MediaRecorder update while camera is closed.", mediaRecoderEventsHa ndler);
529 return;
530 }
531 if (sessionOpening) {
532 reportUpdateMediaRecorderError(
533 "MediaRecorder update while camera is still opening.", mediaRecoderE ventsHandler);
534 return;
535 }
536
537 this.mediaRecorderEventsHandler = mediaRecoderEventsHandler;
538 if (addMediaRecorder) {
sakal 2017/04/26 07:34:12 nit: can be replaced with a ternary operator
AlexG 2017/04/26 21:04:10 Done.
539 mediaRecorderState = MediaRecorderState.IDLE_TO_ACTIVE;
540 } else {
541 mediaRecorderState = MediaRecorderState.ACTIVE_TO_IDLE;
542 }
543 this.mediaRecorder = mediaRecorder;
544
545 Logging.d(TAG, "updateMediaRecoder: Stopping session");
546 cameraStatistics.release();
547 cameraStatistics = null;
548 final CameraSession oldSession = currentSession;
549 cameraThreadHandler.post(new Runnable() {
550 @Override
551 public void run() {
552 oldSession.stop();
553 }
554 });
555 currentSession = null;
556
557 sessionOpening = true;
558 openAttemptsRemaining = 1;
559 createSessionInternal(0);
560 }
561 Logging.d(TAG, "updateMediaRecoderInternal done");
562 }
563
432 private void checkIsOnCameraThread() { 564 private void checkIsOnCameraThread() {
433 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { 565 if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) {
434 Logging.e(TAG, "Check is on camera thread failed."); 566 Logging.e(TAG, "Check is on camera thread failed.");
435 throw new RuntimeException("Not on camera thread."); 567 throw new RuntimeException("Not on camera thread.");
436 } 568 }
437 } 569 }
438 570
439 protected String getCameraName() { 571 protected String getCameraName() {
440 synchronized (stateLock) { 572 synchronized (stateLock) {
441 return cameraName; 573 return cameraName;
442 } 574 }
443 } 575 }
444 576
445 abstract protected void createCameraSession( 577 abstract protected void createCameraSession(
446 CameraSession.CreateSessionCallback createSessionCallback, CameraSession.E vents events, 578 CameraSession.CreateSessionCallback createSessionCallback, CameraSession.E vents events,
447 Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, Str ing cameraName, 579 Context applicationContext, SurfaceTextureHelper surfaceTextureHelper,
448 int width, int height, int framerate); 580 MediaRecorder mediaRecoder, String cameraName, int width, int height, int framerate);
449 } 581 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698