Reland of Support adding and removing MediaRecorder to camera 2 session. (patchset #1 id:1 of https://codereview.webrtc.org/2844233002/ )

Reason for revert:
Revert "Revert of Support adding and removing MediaRecorder to camera 2 session. (patchset #5 id:80001 of https://codereview.webrtc.org/2833773003/ )"

Will fix external bots by cherry picking this CL and updating external functions which depend on CameraVideoCapturer interface

Original issue's description:
> Revert of Support adding and removing MediaRecorder to camera 2 session. (patchset #5 id:80001 of https://codereview.webrtc.org/2833773003/ )
>
> Reason for revert:
> Breaks external bot
>
> Original issue's description:
> > Support adding and removing MediaRecorder to camera 2 session.
> >
> > Camera 1 API is not supported.
> >
> > BUG=b/36684011
> >
> > Review-Url: https://codereview.webrtc.org/2833773003
> > Cr-Commit-Position: refs/heads/master@{#17901}
> > Committed: 2fc04769fa
>
> TBR=sakal@webrtc.org,glaznev@webrtc.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=b/36684011
>
> Review-Url: https://codereview.webrtc.org/2844233002
> Cr-Commit-Position: refs/heads/master@{#17905}
> Committed: 6702739862

TBR=sakal@webrtc.org,magjed@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=b/36684011

Review-Url: https://codereview.webrtc.org/2844393002
Cr-Commit-Position: refs/heads/master@{#17915}
This commit is contained in:
glaznev
2017-04-27 13:38:29 -07:00
committed by Commit bot
parent f0736b16f4
commit 37adc5e81e
7 changed files with 302 additions and 35 deletions

View File

@ -21,11 +21,12 @@ import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.Range;
import android.view.Surface;
import android.view.WindowManager;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
@ -49,6 +50,7 @@ class Camera2Session implements CameraSession {
private final Context applicationContext;
private final CameraManager cameraManager;
private final SurfaceTextureHelper surfaceTextureHelper;
private final Surface mediaRecorderSurface;
private final String cameraId;
private final int width;
private final int height;
@ -123,9 +125,14 @@ class Camera2Session implements CameraSession {
final SurfaceTexture surfaceTexture = surfaceTextureHelper.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(captureFormat.width, captureFormat.height);
surface = new Surface(surfaceTexture);
List<Surface> surfaces = new ArrayList<Surface>();
surfaces.add(surface);
if (mediaRecorderSurface != null) {
Logging.d(TAG, "Add MediaRecorder surface to capture session.");
surfaces.add(mediaRecorderSurface);
}
try {
camera.createCaptureSession(
Arrays.asList(surface), new CaptureSessionCallback(), cameraThreadHandler);
camera.createCaptureSession(surfaces, new CaptureSessionCallback(), cameraThreadHandler);
} catch (CameraAccessException e) {
reportError("Failed to create capture session. " + e);
return;
@ -175,6 +182,10 @@ class Camera2Session implements CameraSession {
chooseFocusMode(captureRequestBuilder);
captureRequestBuilder.addTarget(surface);
if (mediaRecorderSurface != null) {
Logging.d(TAG, "Add MediaRecorder surface to CaptureRequest.Builder");
captureRequestBuilder.addTarget(mediaRecorderSurface);
}
session.setRepeatingRequest(
captureRequestBuilder.build(), new CameraCaptureCallback(), cameraThreadHandler);
} catch (CameraAccessException e) {
@ -280,15 +291,15 @@ class Camera2Session implements CameraSession {
public static void create(CreateSessionCallback callback, Events events,
Context applicationContext, CameraManager cameraManager,
SurfaceTextureHelper surfaceTextureHelper, String cameraId, int width, int height,
int framerate) {
SurfaceTextureHelper surfaceTextureHelper, MediaRecorder mediaRecorder, String cameraId,
int width, int height, int framerate) {
new Camera2Session(callback, events, applicationContext, cameraManager, surfaceTextureHelper,
cameraId, width, height, framerate);
mediaRecorder, cameraId, width, height, framerate);
}
private Camera2Session(CreateSessionCallback callback, Events events, Context applicationContext,
CameraManager cameraManager, SurfaceTextureHelper surfaceTextureHelper, String cameraId,
int width, int height, int framerate) {
CameraManager cameraManager, SurfaceTextureHelper surfaceTextureHelper,
MediaRecorder mediaRecorder, String cameraId, int width, int height, int framerate) {
Logging.d(TAG, "Create new camera2 session on camera " + cameraId);
constructionTimeNs = System.nanoTime();
@ -299,6 +310,7 @@ class Camera2Session implements CameraSession {
this.applicationContext = applicationContext;
this.cameraManager = cameraManager;
this.surfaceTextureHelper = surfaceTextureHelper;
this.mediaRecorderSurface = (mediaRecorder != null) ? mediaRecorder.getSurface() : null;
this.cameraId = cameraId;
this.width = width;
this.height = height;

View File

@ -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);
}
@ -349,6 +383,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);
}