mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Improve handling of missing camera during calls.
This commit is contained in:
committed by
Cody Henthorne
parent
faf0b630c1
commit
340b94f849
@@ -39,11 +39,14 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
private static final String TAG = Log.tag(Camera.class);
|
private static final String TAG = Log.tag(Camera.class);
|
||||||
|
|
||||||
@NonNull private final Context context;
|
@NonNull private final Context context;
|
||||||
@Nullable private final CameraVideoCapturer capturer;
|
@Nullable private CameraVideoCapturer capturer;
|
||||||
@Nullable private CameraEventListener cameraEventListener;
|
@Nullable private CameraEventListener cameraEventListener;
|
||||||
@NonNull private final EglBaseWrapper eglBase;
|
@NonNull private final EglBaseWrapper eglBase;
|
||||||
private final int cameraCount;
|
private final int cameraCount;
|
||||||
@NonNull private CameraState.Direction activeDirection;
|
@NonNull private CameraState.Direction activeDirection;
|
||||||
|
private CameraState.Direction oldActiveDirection;
|
||||||
|
private CapturerObserver observer;
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private boolean isInitialized;
|
private boolean isInitialized;
|
||||||
private int orientation;
|
private int orientation;
|
||||||
@@ -79,6 +82,7 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
@Override
|
@Override
|
||||||
public void initCapturer(@NonNull CapturerObserver observer) {
|
public void initCapturer(@NonNull CapturerObserver observer) {
|
||||||
if (capturer != null) {
|
if (capturer != null) {
|
||||||
|
this.observer = observer; // save in case we need to disposeAndFlipCamera
|
||||||
eglBase.performWithValidEglBase(base -> {
|
eglBase.performWithValidEglBase(base -> {
|
||||||
capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", base.getEglBaseContext()),
|
capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", base.getEglBaseContext()),
|
||||||
context,
|
context,
|
||||||
@@ -99,6 +103,7 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
if (capturer == null || cameraCount < 2) {
|
if (capturer == null || cameraCount < 2) {
|
||||||
throw new AssertionError("Tried to flip the camera, but we only have " + cameraCount + " of them.");
|
throw new AssertionError("Tried to flip the camera, but we only have " + cameraCount + " of them.");
|
||||||
}
|
}
|
||||||
|
oldActiveDirection = activeDirection;
|
||||||
activeDirection = PENDING;
|
activeDirection = PENDING;
|
||||||
capturer.switchCamera(this);
|
capturer.switchCamera(this);
|
||||||
}
|
}
|
||||||
@@ -146,6 +151,28 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disposeAndFlipCamera() {
|
||||||
|
if (capturer != null) {
|
||||||
|
capturer.dispose();
|
||||||
|
boolean wasInitialized = isInitialized;
|
||||||
|
isInitialized = false;
|
||||||
|
CameraState.Direction candidateDirection = oldActiveDirection.switchDirection();
|
||||||
|
CameraVideoCapturer captureCandidate = createVideoCapturer(getCameraEnumerator(context), candidateDirection);
|
||||||
|
if (captureCandidate != null) {
|
||||||
|
capturer = captureCandidate;
|
||||||
|
activeDirection = candidateDirection;
|
||||||
|
if (wasInitialized) {
|
||||||
|
initCapturer(this.observer);
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
setEnabled(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activeDirection = NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int getCount() {
|
int getCount() {
|
||||||
return cameraCount;
|
return cameraCount;
|
||||||
}
|
}
|
||||||
@@ -204,7 +231,9 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
@Override
|
@Override
|
||||||
public void onCameraSwitchError(String errorMessage) {
|
public void onCameraSwitchError(String errorMessage) {
|
||||||
Log.e(TAG, "onCameraSwitchError: " + errorMessage);
|
Log.e(TAG, "onCameraSwitchError: " + errorMessage);
|
||||||
if (cameraEventListener != null) cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
|
if (cameraEventListener != null) {
|
||||||
|
cameraEventListener.onCameraSwitchFailure(new CameraState(getActiveDirection(), getCount()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FilteredCamera2Enumerator extends Camera2Enumerator {
|
private static class FilteredCamera2Enumerator extends Camera2Enumerator {
|
||||||
@@ -314,7 +343,7 @@ public class Camera implements CameraControl, CameraVideoCapturer.CameraSwitchHa
|
|||||||
public void onCapturerStarted(boolean success) {
|
public void onCapturerStarted(boolean success) {
|
||||||
observer.onCapturerStarted(success);
|
observer.onCapturerStarted(success);
|
||||||
if (success && cameraEventListener != null) {
|
if (success && cameraEventListener != null) {
|
||||||
cameraEventListener.onFullyInitialized();
|
cameraEventListener.onFullyInitialized(getCameraState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import androidx.annotation.NonNull;
|
|||||||
* onCameraSwitchCompleted is triggered by {@link org.webrtc.CameraVideoCapturer.CameraSwitchHandler}
|
* onCameraSwitchCompleted is triggered by {@link org.webrtc.CameraVideoCapturer.CameraSwitchHandler}
|
||||||
*/
|
*/
|
||||||
public interface CameraEventListener {
|
public interface CameraEventListener {
|
||||||
void onFullyInitialized();
|
void onFullyInitialized(@NonNull CameraState newCameraState);
|
||||||
void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
|
void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
|
||||||
|
void onCameraSwitchFailure(@NonNull CameraState newCameraState);
|
||||||
void onCameraStopped();
|
void onCameraStopped();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,4 +80,23 @@ public abstract class DeviceAwareActionProcessor extends WebRtcActionProcessor {
|
|||||||
.cameraState(newCameraState)
|
.cameraState(newCameraState)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull WebRtcServiceState handleCameraSwitchFailure(@NonNull WebRtcServiceState currentState, @NonNull CameraState newCameraState) {
|
||||||
|
Log.i(tag, "handleCameraSwitchFailure():");
|
||||||
|
|
||||||
|
BroadcastVideoSink localSink = currentState.getVideoState().getLocalSink();
|
||||||
|
if (localSink != null) {
|
||||||
|
localSink.setRotateToRightSide(false);
|
||||||
|
}
|
||||||
|
if (currentState.getVideoState().getCamera() != null) {
|
||||||
|
// Retry by recreating with the opposite preferred camera
|
||||||
|
currentState.getVideoState().getCamera().disposeAndFlipCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentState.builder()
|
||||||
|
.changeLocalDeviceState()
|
||||||
|
.cameraState(newCameraState)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState;
|
|||||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||||
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
||||||
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.rx.RxStore;
|
import org.thoughtcrime.securesms.util.rx.RxStore;
|
||||||
@@ -988,8 +987,11 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFullyInitialized() {
|
public void onFullyInitialized(@NonNull final CameraState newCameraState) {
|
||||||
process((s, p) -> p.handleOrientationChanged(s, s.getLocalDeviceState().isLandscapeEnabled(), s.getLocalDeviceState().getDeviceOrientation().getDegrees()));
|
process((s, p) -> {
|
||||||
|
WebRtcServiceState s1 = p.handleSetCameraDirection(s, newCameraState);
|
||||||
|
return p.handleOrientationChanged(s1, s.getLocalDeviceState().isLandscapeEnabled(), s.getLocalDeviceState().getDeviceOrientation().getDegrees());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -997,6 +999,11 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
|||||||
process((s, p) -> p.handleCameraSwitchCompleted(s, newCameraState));
|
process((s, p) -> p.handleCameraSwitchCompleted(s, newCameraState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraSwitchFailure(@NonNull final CameraState newCameraState) {
|
||||||
|
process((s, p) -> p.handleCameraSwitchFailure(s, newCameraState));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCameraStopped() {
|
public void onCameraStopped() {
|
||||||
Log.i(TAG, "Camera error. Muting video.");
|
Log.i(TAG, "Camera error. Muting video.");
|
||||||
|
|||||||
@@ -569,6 +569,11 @@ public abstract class WebRtcActionProcessor {
|
|||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull WebRtcServiceState handleCameraSwitchFailure(@NonNull WebRtcServiceState currentState, @NonNull CameraState newCameraState) {
|
||||||
|
Log.i(tag, "handleCameraSwitchFailure not processed");
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) {
|
public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) {
|
||||||
Log.i(tag, "handleNetworkChanged not processed");
|
Log.i(tag, "handleNetworkChanged not processed");
|
||||||
return currentState;
|
return currentState;
|
||||||
@@ -630,6 +635,18 @@ public abstract class WebRtcActionProcessor {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected @NonNull WebRtcServiceState handleSetCameraDirection(@NonNull WebRtcServiceState currentState, CameraState state) {
|
||||||
|
BroadcastVideoSink sink = currentState.getVideoState().getLocalSink();
|
||||||
|
if (sink != null) {
|
||||||
|
sink.setRotateToRightSide(state.getActiveDirection() == CameraState.Direction.BACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentState.builder()
|
||||||
|
.changeLocalDeviceState()
|
||||||
|
.cameraState(state)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
//endregion Local device
|
//endregion Local device
|
||||||
|
|
||||||
//region End call
|
//region End call
|
||||||
|
|||||||
Reference in New Issue
Block a user