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..859b7f745331841016ff78ba52b34a27b1d4af09 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,19 @@ 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; |
+ } |
+ } |
} |
} |
@@ -81,6 +104,14 @@ abstract class CameraCapturer implements CameraVideoCapturer { |
switchState = SwitchState.IDLE; |
} |
+ if (mediaRecorderState != MediaRecorderState.IDLE) { |
+ if (mediaRecorderEventsHandler != null) { |
+ mediaRecorderEventsHandler.onMediaRecorderError(error); |
+ mediaRecorderEventsHandler = null; |
+ } |
+ mediaRecorderState = MediaRecorderState.IDLE; |
+ } |
+ |
if (failureType == CameraSession.FailureType.DISCONNECTED) { |
eventsHandler.onCameraDisconnected(); |
} else { |
@@ -88,8 +119,7 @@ abstract class CameraCapturer implements CameraVideoCapturer { |
} |
} else { |
Logging.w(TAG, "Opening camera failed, retry: " + error); |
- |
- createSessionInternal(OPEN_CAMERA_DELAY_MS); |
+ createSessionInternal(OPEN_CAMERA_DELAY_MS, null /* mediaRecorder */); |
} |
} |
} |
@@ -213,6 +243,10 @@ 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; |
+ |
public CameraCapturer( |
String cameraName, CameraEventsHandler eventsHandler, CameraEnumerator cameraEnumerator) { |
if (eventsHandler == null) { |
@@ -277,17 +311,17 @@ abstract class CameraCapturer implements CameraVideoCapturer { |
sessionOpening = true; |
openAttemptsRemaining = MAX_OPEN_CAMERA_ATTEMPTS; |
- createSessionInternal(0); |
+ createSessionInternal(0, null /* mediaRecorder */); |
} |
} |
- private void createSessionInternal(int delayMs) { |
+ private void createSessionInternal(int delayMs, final MediaRecorder mediaRecorder) { |
uiThreadHandler.postDelayed(openCameraTimeoutRunnable, delayMs + OPEN_CAMERA_TIMEOUT); |
cameraThreadHandler.postDelayed(new Runnable() { |
@Override |
public void run() { |
createCameraSession(createSessionCallback, cameraSessionEventsHandler, applicationContext, |
- surfaceHelper, cameraName, width, height, framerate); |
+ surfaceHelper, mediaRecorder, cameraName, width, height, framerate); |
} |
}, delayMs); |
} |
@@ -350,6 +384,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 +427,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 +448,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; |
} |
@@ -424,11 +485,74 @@ abstract class CameraCapturer implements CameraVideoCapturer { |
sessionOpening = true; |
openAttemptsRemaining = 1; |
- createSessionInternal(0); |
+ createSessionInternal(0, null /* mediaRecorder */); |
} |
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; |
+ mediaRecorderState = |
+ addMediaRecorder ? MediaRecorderState.IDLE_TO_ACTIVE : MediaRecorderState.ACTIVE_TO_IDLE; |
+ |
+ 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, mediaRecorder); |
+ } |
+ 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 +568,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); |
} |