Chromium Code Reviews| Index: webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java | 
| diff --git a/webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java b/webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java | 
| index c3e8daab17cd857f8f5a968f0fdc86ab94c20e63..127df3b20f0f035fad51101954a487f6eefb9333 100644 | 
| --- a/webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java | 
| +++ b/webrtc/sdk/android/src/java/org/webrtc/CameraCapturer.java | 
| @@ -11,6 +11,7 @@ | 
| package org.webrtc; | 
| import android.content.Context; | 
| +import android.media.MediaRecorder; | 
| import android.os.Handler; | 
| import android.os.Looper; | 
| import java.util.Arrays; | 
| @@ -23,6 +24,13 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| IN_PROGRESS, // Waiting for new switched capture session to start. | 
| } | 
| + enum MediaRecorderState { | 
| + IDLE, // No media recording update (add or remove) requested. | 
| + IDLE_TO_ACTIVE, // Waiting for new capture session with added MediaRecorder surface to start. | 
| + ACTIVE_TO_IDLE, // Waiting for new capture session with removed MediaRecorder surface to start. | 
| + ACTIVE, // MediaRecorder was successfully added to camera pipeline. | 
| + } | 
| + | 
| private static final String TAG = "CameraCapturer"; | 
| private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; | 
| private final static int OPEN_CAMERA_DELAY_MS = 500; | 
| @@ -37,7 +45,9 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| @Override | 
| public void onDone(CameraSession session) { | 
| checkIsOnCameraThread(); | 
| - Logging.d(TAG, "Create session done"); | 
| + Logging.d(TAG, | 
| + "Create session done. Switch state: " + switchState | 
| + + ". MediaRecorder state: " + mediaRecorderState); | 
| uiThreadHandler.removeCallbacks(openCameraTimeoutRunnable); | 
| synchronized (stateLock) { | 
| capturerObserver.onCapturerStarted(true /* success */); | 
| @@ -57,6 +67,20 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| switchState = SwitchState.IDLE; | 
| switchCameraInternal(switchEventsHandler); | 
| } | 
| + | 
| + if (mediaRecorderState == MediaRecorderState.IDLE_TO_ACTIVE | 
| + || mediaRecorderState == MediaRecorderState.ACTIVE_TO_IDLE) { | 
| + if (mediaRecorderEventsHandler != null) { | 
| + mediaRecorderEventsHandler.onMediaRecorderSuccess(); | 
| + mediaRecorderEventsHandler = null; | 
| + } | 
| + if (mediaRecorderState == MediaRecorderState.IDLE_TO_ACTIVE) { | 
| + mediaRecorderState = MediaRecorderState.ACTIVE; | 
| + } else { | 
| + mediaRecorderState = MediaRecorderState.IDLE; | 
| + } | 
| + mediaRecorder = null; | 
| + } | 
| } | 
| } | 
| @@ -81,6 +105,15 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| switchState = SwitchState.IDLE; | 
| } | 
| + if (mediaRecorderState != MediaRecorderState.IDLE) { | 
| + if (mediaRecorderEventsHandler != null) { | 
| + mediaRecorderEventsHandler.onMediaRecorderError(error); | 
| + mediaRecorderEventsHandler = null; | 
| + } | 
| + mediaRecorderState = MediaRecorderState.IDLE; | 
| + mediaRecorder = null; | 
| + } | 
| + | 
| if (failureType == CameraSession.FailureType.DISCONNECTED) { | 
| eventsHandler.onCameraDisconnected(); | 
| } else { | 
| @@ -213,6 +246,11 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| private CameraStatistics cameraStatistics; /* guarded by stateLock */ | 
| private boolean firstFrameObserved; /* guarded by stateLock */ | 
| + // Variables used on camera thread - do not require stateLock synchronization. | 
| + private MediaRecorderState mediaRecorderState = MediaRecorderState.IDLE; | 
| + private MediaRecorderHandler mediaRecorderEventsHandler; | 
| + private MediaRecorder mediaRecorder; | 
| + | 
| public CameraCapturer( | 
| String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cameraEnumerator) { | 
| if (eventsHandler == null) { | 
| @@ -287,7 +325,7 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| @Override | 
| public void run() { | 
| createCameraSession(createSessionCallback, cameraSessionEventsHandler, applicationContext, | 
| - surfaceHelper, cameraName, width, height, framerate); | 
| + surfaceHelper, mediaRecorder, cameraName, width, height, framerate); | 
| } | 
| }, delayMs); | 
| } | 
| @@ -350,6 +388,29 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| } | 
| @Override | 
| + public void addMediaRecorderToCamera( | 
| + final MediaRecorder mediaRecorder, final MediaRecorderHandler mediaRecoderEventsHandler) { | 
| + Logging.d(TAG, "addMediaRecorderToCamera"); | 
| + cameraThreadHandler.post(new Runnable() { | 
| + @Override | 
| + public void run() { | 
| + updateMediaRecorderInternal(mediaRecorder, mediaRecoderEventsHandler); | 
| + } | 
| + }); | 
| + } | 
| + | 
| + @Override | 
| + public void removeMediaRecorderFromCamera(final MediaRecorderHandler mediaRecoderEventsHandler) { | 
| + Logging.d(TAG, "removeMediaRecorderFromCamera"); | 
| + cameraThreadHandler.post(new Runnable() { | 
| + @Override | 
| + public void run() { | 
| + updateMediaRecorderInternal(null /* mediaRecorder */, mediaRecoderEventsHandler); | 
| + } | 
| + }); | 
| + } | 
| + | 
| + @Override | 
| public boolean isScreencast() { | 
| return false; | 
| } | 
| @@ -370,6 +431,13 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| } | 
| } | 
| + private void reportCameraSwitchError(String error, CameraSwitchHandler switchEventsHandler) { | 
| + Logging.e(TAG, error); | 
| + if (switchEventsHandler != null) { | 
| + switchEventsHandler.onCameraSwitchError(error); | 
| + } | 
| + } | 
| + | 
| private void switchCameraInternal(final CameraSwitchHandler switchEventsHandler) { | 
| Logging.d(TAG, "switchCamera internal"); | 
| @@ -384,18 +452,15 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| synchronized (stateLock) { | 
| if (switchState != SwitchState.IDLE) { | 
| - Logging.d(TAG, "switchCamera switchInProgress"); | 
| - if (switchEventsHandler != null) { | 
| - switchEventsHandler.onCameraSwitchError("Camera switch already in progress."); | 
| - } | 
| + reportCameraSwitchError("Camera switch already in progress.", switchEventsHandler); | 
| + return; | 
| + } | 
| + if (mediaRecorderState != MediaRecorderState.IDLE) { | 
| + reportCameraSwitchError("switchCamera: media recording is active", switchEventsHandler); | 
| return; | 
| } | 
| - | 
| if (!sessionOpening && currentSession == null) { | 
| - Logging.d(TAG, "switchCamera: No session open"); | 
| - if (switchEventsHandler != null) { | 
| - switchEventsHandler.onCameraSwitchError("Camera is not running."); | 
| - } | 
| + reportCameraSwitchError("switchCamera: camera is not running.", switchEventsHandler); | 
| return; | 
| } | 
| @@ -429,6 +494,73 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| Logging.d(TAG, "switchCamera done"); | 
| } | 
| + private void reportUpdateMediaRecorderError( | 
| + String error, MediaRecorderHandler mediaRecoderEventsHandler) { | 
| + checkIsOnCameraThread(); | 
| + Logging.e(TAG, error); | 
| + if (mediaRecoderEventsHandler != null) { | 
| + mediaRecoderEventsHandler.onMediaRecorderError(error); | 
| + } | 
| + } | 
| + | 
| + private void updateMediaRecorderInternal( | 
| + MediaRecorder mediaRecorder, MediaRecorderHandler mediaRecoderEventsHandler) { | 
| + checkIsOnCameraThread(); | 
| + boolean addMediaRecorder = (mediaRecorder != null); | 
| + Logging.d(TAG, | 
| + "updateMediaRecoderInternal internal. State: " + mediaRecorderState | 
| + + ". Switch state: " + switchState + ". Add MediaRecorder: " + addMediaRecorder); | 
| + | 
| + synchronized (stateLock) { | 
| + if ((addMediaRecorder && mediaRecorderState != MediaRecorderState.IDLE) | 
| + || (!addMediaRecorder && mediaRecorderState != MediaRecorderState.ACTIVE)) { | 
| + reportUpdateMediaRecorderError( | 
| + "Incorrect state for MediaRecorder update.", mediaRecoderEventsHandler); | 
| + return; | 
| + } | 
| + if (switchState != SwitchState.IDLE) { | 
| + reportUpdateMediaRecorderError( | 
| + "MediaRecorder update while camera is switching.", mediaRecoderEventsHandler); | 
| + return; | 
| + } | 
| + if (currentSession == null) { | 
| + reportUpdateMediaRecorderError( | 
| + "MediaRecorder update while camera is closed.", mediaRecoderEventsHandler); | 
| + return; | 
| + } | 
| + if (sessionOpening) { | 
| + reportUpdateMediaRecorderError( | 
| + "MediaRecorder update while camera is still opening.", mediaRecoderEventsHandler); | 
| + return; | 
| + } | 
| + | 
| + this.mediaRecorderEventsHandler = mediaRecoderEventsHandler; | 
| + 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.
 
 | 
| + mediaRecorderState = MediaRecorderState.IDLE_TO_ACTIVE; | 
| + } else { | 
| + mediaRecorderState = MediaRecorderState.ACTIVE_TO_IDLE; | 
| + } | 
| + this.mediaRecorder = mediaRecorder; | 
| + | 
| + Logging.d(TAG, "updateMediaRecoder: Stopping session"); | 
| + cameraStatistics.release(); | 
| + cameraStatistics = null; | 
| + final CameraSession oldSession = currentSession; | 
| + cameraThreadHandler.post(new Runnable() { | 
| + @Override | 
| + public void run() { | 
| + oldSession.stop(); | 
| + } | 
| + }); | 
| + currentSession = null; | 
| + | 
| + sessionOpening = true; | 
| + openAttemptsRemaining = 1; | 
| + createSessionInternal(0); | 
| + } | 
| + Logging.d(TAG, "updateMediaRecoderInternal done"); | 
| + } | 
| + | 
| private void checkIsOnCameraThread() { | 
| if (Thread.currentThread() != cameraThreadHandler.getLooper().getThread()) { | 
| Logging.e(TAG, "Check is on camera thread failed."); | 
| @@ -444,6 +576,6 @@ abstract class CameraCapturer implements CameraVideoCapturer { | 
| abstract protected void createCameraSession( | 
| CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events, | 
| - Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName, | 
| - int width, int height, int framerate); | 
| + Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, | 
| + MediaRecorder mediaRecoder, String cameraName, int width, int height, int framerate); | 
| } |