Skip monochrome cameras when switching cameras on video calls.

master
Curt Brune 2019-12-13 14:01:13 -08:00 committed by Alan Evans
parent ffa01d491a
commit 284fe294ac
3 changed files with 116 additions and 4 deletions

View File

@ -117,7 +117,7 @@ dependencies {
implementation project(':libsignal-service')
implementation 'org.signal:ringrtc-android:0.3.0'
implementation 'org.signal:ringrtc-android:0.3.1'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'

View File

@ -351,8 +351,8 @@ dependencyVerification {
['org.signal:android-database-sqlcipher:3.5.9-S3',
'33d4063336893af00b9d68b418e7b290cace74c20ce8aacffddc0911010d3d73'],
['org.signal:ringrtc-android:0.3.0',
'c3c294915f86d6dbaf934d6542e3f99a003c50f5490bd2c0b80f5fe3a7a5fdfc'],
['org.signal:ringrtc-android:0.3.1',
'785c422a2322f810141d85f63eab3874961b19face886daaf140ab011d39e166'],
['org.signal:signal-metadata-java:0.1.0',
'f3faa23b7d9b5096d12979c35679d1e3b5e007522d8bef167a28e456f2a7c7d9'],

View File

@ -1,11 +1,20 @@
package org.thoughtcrime.securesms.ringrtc;
import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import java.io.IOException;
import java.util.List;
import java.util.LinkedList;
import org.signal.ringrtc.CallConnection;
import org.signal.ringrtc.CallConnectionFactory;
@ -17,6 +26,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Capturer;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.CameraVideoCapturer;
@ -265,7 +275,7 @@ public class CallConnectionWrapper {
Log.i(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
return camera2EnumeratorIsSupported ? new Camera2Enumerator(context)
return camera2EnumeratorIsSupported ? new FilteredCamera2Enumerator(context)
: new Camera1Enumerator(true);
}
@ -285,4 +295,106 @@ public class CallConnectionWrapper {
public interface CameraEventListener {
void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
}
@TargetApi(21)
private static class FilteredCamera2Enumerator extends Camera2Enumerator {
@NonNull private final Context context;
@Nullable private final CameraManager cameraManager;
@Nullable private String[] deviceNames;
FilteredCamera2Enumerator(@NonNull Context context) {
super(context);
this.context = context;
this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
this.deviceNames = null;
}
private boolean isMonochrome(String deviceName, CameraManager cameraManager) {
try {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(deviceName);
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
if (capabilities != null) {
for (int cap : capabilities) {
if (cap == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
return true;
}
}
}
} catch (CameraAccessException e) {
return false;
}
return false;
}
private boolean isLensFacing(String deviceName, CameraManager cameraManager, Integer facing) {
try {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(deviceName);
Integer lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
return facing.equals(lensFacing);
} catch (CameraAccessException e) {
return false;
}
}
@Override
public @NonNull String[] getDeviceNames() {
if (deviceNames != null) {
return deviceNames;
}
try {
List<String> cameraList = new LinkedList<>();
if (cameraManager != null) {
// While skipping cameras that are monochrome, gather cameras
// until we have at most 1 front facing camera and 1 back
// facing camera.
List<String> devices = Stream.of(cameraManager.getCameraIdList())
.filterNot(id -> isMonochrome(id, cameraManager))
.toList();
String frontCamera = Stream.of(devices)
.filter(id -> isLensFacing(id, cameraManager, CameraMetadata.LENS_FACING_FRONT))
.findFirst()
.orElse(null);
if (frontCamera != null) {
cameraList.add(frontCamera);
}
String backCamera = Stream.of(devices)
.filter(id -> isLensFacing(id, cameraManager, CameraMetadata.LENS_FACING_BACK))
.findFirst()
.orElse(null);
if (backCamera != null) {
cameraList.add(backCamera);
}
}
this.deviceNames = cameraList.toArray(new String[0]);
} catch (CameraAccessException e) {
Log.e(TAG, "Camera access exception: " + e);
this.deviceNames = new String[] {};
}
return deviceNames;
}
@Override
public @NonNull CameraVideoCapturer createCapturer(@Nullable String deviceName,
@Nullable CameraVideoCapturer.CameraEventsHandler eventsHandler) {
return new Camera2Capturer(context, deviceName, eventsHandler, new FilteredCamera2Enumerator(context));
}
}
}