Revert "Add ringrtc support."
Revert the following commits: "Handle busy call while in PSTN call." Commit23a0bb3ce0
. "Add ringrtc support." Commit3ac540c687
.
This commit is contained in:
parent
72662b5b52
commit
97cc8b7777
12 changed files with 3140 additions and 781 deletions
|
@ -88,7 +88,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'org.whispersystems:signal-service-android:2.13.7'
|
implementation 'org.whispersystems:signal-service-android:2.13.7'
|
||||||
|
|
||||||
implementation 'org.signal:ringrtc-android:0.1.1'
|
implementation 'org.whispersystems:webrtc-android:M75'
|
||||||
|
|
||||||
implementation "me.leolin:ShortcutBadger:1.1.16"
|
implementation "me.leolin:ShortcutBadger:1.1.16"
|
||||||
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
|
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
|
||||||
|
@ -198,7 +198,7 @@ dependencyVerification {
|
||||||
'org.conscrypt:conscrypt-android:400ca559a49b860a82862b22cee0e3110764bdcf7ee7c79e7479895c25cdfc09',
|
'org.conscrypt:conscrypt-android:400ca559a49b860a82862b22cee0e3110764bdcf7ee7c79e7479895c25cdfc09',
|
||||||
'org.signal:aesgcmprovider:6eb4422e8a618b3b76cb2096a3619d251f9e27989dc68307a1e5414c3710f2d1',
|
'org.signal:aesgcmprovider:6eb4422e8a618b3b76cb2096a3619d251f9e27989dc68307a1e5414c3710f2d1',
|
||||||
'org.whispersystems:signal-service-android:5115aa434c52ca671c513995e6ae67d73f3abaaa605f9e6cf64c2e01da961c7e',
|
'org.whispersystems:signal-service-android:5115aa434c52ca671c513995e6ae67d73f3abaaa605f9e6cf64c2e01da961c7e',
|
||||||
'org.signal:ringrtc-android:91d4d89847c10e718bc8badfa0353f0095678855c35c2575509fea97f615de86',
|
'org.whispersystems:webrtc-android:f8231bb57923afb243760213dc58924e85cce42f2f3cc8cb33a6d883672a921a',
|
||||||
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
|
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
|
||||||
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
||||||
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
|
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
|
||||||
|
|
3
proguard-webrtc.pro
Normal file
3
proguard-webrtc.pro
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
-dontwarn org.webrtc.NetworkMonitorAutoDetect
|
||||||
|
-dontwarn android.net.Network
|
||||||
|
-keep class org.webrtc.** { *; }
|
|
@ -33,7 +33,6 @@ import com.google.android.gms.security.ProviderInstaller;
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
import org.conscrypt.Conscrypt;
|
||||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||||
import org.signal.ringrtc.CallConnectionFactory;
|
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
@ -71,6 +70,8 @@ import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||||
|
import org.webrtc.PeerConnectionFactory;
|
||||||
|
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||||
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
|
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
|
||||||
|
@ -125,7 +126,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||||
initializeSignedPreKeyCheck();
|
initializeSignedPreKeyCheck();
|
||||||
initializePeriodicTasks();
|
initializePeriodicTasks();
|
||||||
initializeCircumvention();
|
initializeCircumvention();
|
||||||
initializeRingRtc();
|
initializeWebRtc();
|
||||||
initializePendingMessages();
|
initializePendingMessages();
|
||||||
initializeUnidentifiedDeliveryAbilityRefresh();
|
initializeUnidentifiedDeliveryAbilityRefresh();
|
||||||
initializeBlobProvider();
|
initializeBlobProvider();
|
||||||
|
@ -281,7 +282,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeRingRtc() {
|
private void initializeWebRtc() {
|
||||||
try {
|
try {
|
||||||
Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {{
|
Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {{
|
||||||
add("Pixel");
|
add("Pixel");
|
||||||
|
@ -310,7 +311,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||||
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
|
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
CallConnectionFactory.initialize(this);
|
PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions());
|
||||||
} catch (UnsatisfiedLinkError e) {
|
} catch (UnsatisfiedLinkError e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,10 @@ import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||||
import org.thoughtcrime.securesms.ringrtc.CameraState;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
|
import org.thoughtcrime.securesms.webrtc.CameraState;
|
||||||
import org.webrtc.SurfaceViewRenderer;
|
import org.webrtc.SurfaceViewRenderer;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.ringrtc.CameraState;
|
import org.thoughtcrime.securesms.webrtc.CameraState;
|
||||||
import org.webrtc.SurfaceViewRenderer;
|
import org.webrtc.SurfaceViewRenderer;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
|
|
||||||
|
|
|
@ -1,293 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.ringrtc;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.signal.ringrtc.CallConnection;
|
|
||||||
import org.signal.ringrtc.CallConnectionFactory;
|
|
||||||
import org.signal.ringrtc.CallException;
|
|
||||||
import org.signal.ringrtc.SignalMessageRecipient;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
|
|
||||||
import org.webrtc.AudioSource;
|
|
||||||
import org.webrtc.AudioTrack;
|
|
||||||
import org.webrtc.Camera1Enumerator;
|
|
||||||
import org.webrtc.Camera2Enumerator;
|
|
||||||
import org.webrtc.CameraEnumerator;
|
|
||||||
import org.webrtc.CameraVideoCapturer;
|
|
||||||
import org.webrtc.EglBase;
|
|
||||||
import org.webrtc.IceCandidate;
|
|
||||||
import org.webrtc.MediaConstraints;
|
|
||||||
import org.webrtc.MediaStream;
|
|
||||||
import org.webrtc.SurfaceTextureHelper;
|
|
||||||
import org.webrtc.VideoSink;
|
|
||||||
import org.webrtc.VideoSource;
|
|
||||||
import org.webrtc.VideoTrack;
|
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.ringrtc.CameraState.Direction.BACK;
|
|
||||||
import static org.thoughtcrime.securesms.ringrtc.CameraState.Direction.FRONT;
|
|
||||||
import static org.thoughtcrime.securesms.ringrtc.CameraState.Direction.NONE;
|
|
||||||
import static org.thoughtcrime.securesms.ringrtc.CameraState.Direction.PENDING;
|
|
||||||
|
|
||||||
public class CallConnectionWrapper {
|
|
||||||
private static final String TAG = Log.tag(CallConnectionWrapper.class);
|
|
||||||
|
|
||||||
@NonNull private final CallConnection callConnection;
|
|
||||||
@NonNull private final AudioTrack audioTrack;
|
|
||||||
@NonNull private final AudioSource audioSource;
|
|
||||||
@NonNull private final Camera camera;
|
|
||||||
@Nullable private final VideoSource videoSource;
|
|
||||||
@Nullable private final VideoTrack videoTrack;
|
|
||||||
|
|
||||||
public CallConnectionWrapper(@NonNull Context context,
|
|
||||||
@NonNull CallConnectionFactory factory,
|
|
||||||
@NonNull CallConnection.Observer observer,
|
|
||||||
@NonNull VideoSink localRenderer,
|
|
||||||
@NonNull CameraEventListener cameraEventListener,
|
|
||||||
@NonNull EglBase eglBase,
|
|
||||||
boolean hideIp,
|
|
||||||
long callId,
|
|
||||||
boolean outBound,
|
|
||||||
@NonNull SignalMessageRecipient recipient,
|
|
||||||
@NonNull SignalServiceAccountManager accountManager)
|
|
||||||
throws UnregisteredUserException, IOException, CallException
|
|
||||||
{
|
|
||||||
|
|
||||||
CallConnection.Configuration configuration = new CallConnection.Configuration(callId,
|
|
||||||
outBound,
|
|
||||||
recipient,
|
|
||||||
accountManager,
|
|
||||||
hideIp);
|
|
||||||
|
|
||||||
this.callConnection = factory.createCallConnection(configuration, observer);
|
|
||||||
this.callConnection.setAudioPlayout(false);
|
|
||||||
this.callConnection.setAudioRecording(false);
|
|
||||||
|
|
||||||
MediaStream mediaStream = factory.createLocalMediaStream("ARDAMS");
|
|
||||||
MediaConstraints audioConstraints = new MediaConstraints();
|
|
||||||
|
|
||||||
audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
|
||||||
this.audioSource = factory.createAudioSource(audioConstraints);
|
|
||||||
this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
|
|
||||||
this.audioTrack.setEnabled(false);
|
|
||||||
mediaStream.addTrack(audioTrack);
|
|
||||||
|
|
||||||
this.camera = new Camera(context, cameraEventListener);
|
|
||||||
|
|
||||||
if (camera.capturer != null) {
|
|
||||||
this.videoSource = factory.createVideoSource(false);
|
|
||||||
this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
|
|
||||||
|
|
||||||
camera.capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", eglBase.getEglBaseContext()), context, videoSource.getCapturerObserver());
|
|
||||||
|
|
||||||
this.videoTrack.addSink(localRenderer);
|
|
||||||
this.videoTrack.setEnabled(false);
|
|
||||||
mediaStream.addTrack(videoTrack);
|
|
||||||
} else {
|
|
||||||
this.videoSource = null;
|
|
||||||
this.videoTrack = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.callConnection.addStream(mediaStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addIceCandidate(IceCandidate candidate) {
|
|
||||||
return callConnection.addIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendOffer() throws CallException {
|
|
||||||
callConnection.sendOffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean validateResponse(SignalMessageRecipient recipient, Long inCallId)
|
|
||||||
throws CallException
|
|
||||||
{
|
|
||||||
return callConnection.validateResponse(recipient, inCallId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleOfferAnswer(String sessionDescription) throws CallException {
|
|
||||||
callConnection.handleOfferAnswer(sessionDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void acceptOffer(String offer) throws CallException {
|
|
||||||
callConnection.acceptOffer(offer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hangUp() throws CallException {
|
|
||||||
callConnection.hangUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void answerCall() throws CallException {
|
|
||||||
callConnection.answerCall();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendBusy(SignalMessageRecipient recipient, Long inCallId) throws CallException
|
|
||||||
{
|
|
||||||
callConnection.sendBusy(recipient, inCallId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoEnabled(boolean enabled) throws CallException {
|
|
||||||
if (videoTrack != null) {
|
|
||||||
videoTrack.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
camera.setEnabled(enabled);
|
|
||||||
callConnection.sendVideoStatus(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flipCamera() {
|
|
||||||
camera.flip();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CameraState getCameraState() {
|
|
||||||
return new CameraState(camera.getActiveDirection(), camera.getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCommunicationMode() {
|
|
||||||
callConnection.setAudioPlayout(true);
|
|
||||||
callConnection.setAudioRecording(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAudioEnabled(boolean enabled) {
|
|
||||||
audioTrack.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
camera.dispose();
|
|
||||||
|
|
||||||
if (videoSource != null) {
|
|
||||||
videoSource.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
audioSource.dispose();
|
|
||||||
callConnection.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Camera implements CameraVideoCapturer.CameraSwitchHandler {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private final CameraVideoCapturer capturer;
|
|
||||||
private final CameraEventListener cameraEventListener;
|
|
||||||
private final int cameraCount;
|
|
||||||
|
|
||||||
private CameraState.Direction activeDirection;
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
Camera(@NonNull Context context, @NonNull CameraEventListener cameraEventListener)
|
|
||||||
{
|
|
||||||
this.cameraEventListener = cameraEventListener;
|
|
||||||
CameraEnumerator enumerator = getCameraEnumerator(context);
|
|
||||||
cameraCount = enumerator.getDeviceNames().length;
|
|
||||||
|
|
||||||
CameraVideoCapturer capturerCandidate = createVideoCapturer(enumerator, FRONT);
|
|
||||||
if (capturerCandidate != null) {
|
|
||||||
activeDirection = FRONT;
|
|
||||||
} else {
|
|
||||||
capturerCandidate = createVideoCapturer(enumerator, BACK);
|
|
||||||
if (capturerCandidate != null) {
|
|
||||||
activeDirection = BACK;
|
|
||||||
} else {
|
|
||||||
activeDirection = NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
capturer = capturerCandidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flip() {
|
|
||||||
if (capturer == null || cameraCount < 2) {
|
|
||||||
throw new AssertionError("Tried to flip the camera, but we only have " + cameraCount +
|
|
||||||
" of them.");
|
|
||||||
}
|
|
||||||
activeDirection = PENDING;
|
|
||||||
capturer.switchCamera(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
|
|
||||||
if (capturer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (enabled) {
|
|
||||||
capturer.startCapture(1280, 720, 30);
|
|
||||||
} else {
|
|
||||||
capturer.stopCapture();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.w(TAG, "Got interrupted while trying to stop video capture", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
if (capturer != null) {
|
|
||||||
capturer.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int getCount() {
|
|
||||||
return cameraCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull CameraState.Direction getActiveDirection() {
|
|
||||||
return enabled ? activeDirection : NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable CameraVideoCapturer getCapturer() {
|
|
||||||
return capturer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull CameraEnumerator enumerator,
|
|
||||||
@NonNull CameraState.Direction direction)
|
|
||||||
{
|
|
||||||
String[] deviceNames = enumerator.getDeviceNames();
|
|
||||||
for (String deviceName : deviceNames) {
|
|
||||||
if ((direction == FRONT && enumerator.isFrontFacing(deviceName)) ||
|
|
||||||
(direction == BACK && enumerator.isBackFacing(deviceName)))
|
|
||||||
{
|
|
||||||
return enumerator.createCapturer(deviceName, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NonNull CameraEnumerator getCameraEnumerator(@NonNull Context context) {
|
|
||||||
boolean camera2EnumeratorIsSupported = false;
|
|
||||||
try {
|
|
||||||
camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
|
|
||||||
} catch (final Throwable throwable) {
|
|
||||||
Log.w(TAG, "Camera2Enumator.isSupport() threw.", throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
|
|
||||||
|
|
||||||
return camera2EnumeratorIsSupported ? new Camera2Enumerator(context)
|
|
||||||
: new Camera1Enumerator(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCameraSwitchDone(boolean isFrontFacing) {
|
|
||||||
activeDirection = isFrontFacing ? FRONT : BACK;
|
|
||||||
cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCameraSwitchError(String errorMessage) {
|
|
||||||
Log.e(TAG, "onCameraSwitchError: " + errorMessage);
|
|
||||||
cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface CameraEventListener {
|
|
||||||
void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.ringrtc;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.signal.ringrtc.SignalMessageRecipient;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
|
||||||
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
|
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
|
||||||
|
|
||||||
public final class MessageRecipient implements SignalMessageRecipient {
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(MessageRecipient.class);
|
|
||||||
|
|
||||||
@NonNull private final Recipient recipient;
|
|
||||||
@NonNull private final SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MessageRecipient(SignalServiceMessageSender messageSender,
|
|
||||||
Recipient recipient)
|
|
||||||
{
|
|
||||||
this.recipient = recipient;
|
|
||||||
this.messageSender = messageSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull Address getAddress() {
|
|
||||||
return recipient.getAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEqual(@NonNull SignalMessageRecipient o) {
|
|
||||||
if (!(o instanceof MessageRecipient)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageRecipient that = (MessageRecipient) o;
|
|
||||||
|
|
||||||
return recipient.equals(that.recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendMessage(Context context, SignalServiceCallMessage callMessage)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()),
|
|
||||||
UnidentifiedAccessUtil.getAccessFor(context, recipient),
|
|
||||||
callMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendOfferMessage(Context context, long callId, String description)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
Log.i(TAG, "MessageRecipient::sendOfferMessage(): callId: " + callId);
|
|
||||||
|
|
||||||
OfferMessage offerMessage = new OfferMessage(callId, description);
|
|
||||||
sendMessage(context, SignalServiceCallMessage.forOffer(offerMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendAnswerMessage(Context context, long callId, String description)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
Log.i(TAG, "MessageRecipient::sendAnswerMessage(): callId: " + callId);
|
|
||||||
|
|
||||||
AnswerMessage answerMessage = new AnswerMessage(callId, description);
|
|
||||||
sendMessage(context, SignalServiceCallMessage.forAnswer(answerMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendIceUpdates(Context context, List<IceUpdateMessage> iceUpdateMessages)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
Log.i(TAG, "MessageRecipient::sendIceUpdates(): iceUpdates: " + iceUpdateMessages.size());
|
|
||||||
|
|
||||||
sendMessage(context, SignalServiceCallMessage.forIceUpdates(iceUpdateMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendHangupMessage(Context context, long callId)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
Log.i(TAG, "MessageRecipient::sendHangupMessage(): callId: " + callId);
|
|
||||||
|
|
||||||
HangupMessage hangupMessage = new HangupMessage(callId);
|
|
||||||
sendMessage(context, SignalServiceCallMessage.forHangup(hangupMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendBusyMessage(Context context, long callId)
|
|
||||||
throws IOException, UntrustedIdentityException, IOException
|
|
||||||
{
|
|
||||||
Log.i(TAG, "MessageRecipient::sendBusyMessage(): callId: " + callId);
|
|
||||||
|
|
||||||
BusyMessage busyMessage = new BusyMessage(callId);
|
|
||||||
sendMessage(context, SignalServiceCallMessage.forBusy(busyMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
package org.thoughtcrime.securesms.ringrtc;
|
package org.thoughtcrime.securesms.webrtc;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.thoughtcrime.securesms.webrtc;
|
||||||
|
|
||||||
|
|
||||||
|
import org.webrtc.PeerConnectionFactory;
|
||||||
|
|
||||||
|
public class PeerConnectionFactoryOptions extends PeerConnectionFactory.Options {
|
||||||
|
|
||||||
|
public PeerConnectionFactoryOptions() {
|
||||||
|
this.networkIgnoreMask = 1 << 4;
|
||||||
|
}
|
||||||
|
}
|
424
src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java
Normal file
424
src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java
Normal file
|
@ -0,0 +1,424 @@
|
||||||
|
package org.thoughtcrime.securesms.webrtc;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
||||||
|
import org.webrtc.AudioSource;
|
||||||
|
import org.webrtc.AudioTrack;
|
||||||
|
import org.webrtc.Camera1Enumerator;
|
||||||
|
import org.webrtc.Camera2Enumerator;
|
||||||
|
import org.webrtc.CameraEnumerator;
|
||||||
|
import org.webrtc.CameraVideoCapturer;
|
||||||
|
import org.webrtc.DataChannel;
|
||||||
|
import org.webrtc.EglBase;
|
||||||
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.MediaConstraints;
|
||||||
|
import org.webrtc.MediaStream;
|
||||||
|
import org.webrtc.PeerConnection;
|
||||||
|
import org.webrtc.PeerConnectionFactory;
|
||||||
|
import org.webrtc.SdpObserver;
|
||||||
|
import org.webrtc.SessionDescription;
|
||||||
|
import org.webrtc.SurfaceTextureHelper;
|
||||||
|
import org.webrtc.VideoSink;
|
||||||
|
import org.webrtc.VideoSource;
|
||||||
|
import org.webrtc.VideoTrack;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import static org.thoughtcrime.securesms.webrtc.CameraState.Direction.BACK;
|
||||||
|
import static org.thoughtcrime.securesms.webrtc.CameraState.Direction.FRONT;
|
||||||
|
import static org.thoughtcrime.securesms.webrtc.CameraState.Direction.NONE;
|
||||||
|
import static org.thoughtcrime.securesms.webrtc.CameraState.Direction.PENDING;
|
||||||
|
|
||||||
|
public class PeerConnectionWrapper {
|
||||||
|
private static final String TAG = PeerConnectionWrapper.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final PeerConnection.IceServer STUN_SERVER = new PeerConnection.IceServer("stun:stun1.l.google.com:19302");
|
||||||
|
|
||||||
|
@NonNull private final PeerConnection peerConnection;
|
||||||
|
@NonNull private final AudioTrack audioTrack;
|
||||||
|
@NonNull private final AudioSource audioSource;
|
||||||
|
@NonNull private final Camera camera;
|
||||||
|
@Nullable private final VideoSource videoSource;
|
||||||
|
@Nullable private final VideoTrack videoTrack;
|
||||||
|
|
||||||
|
public PeerConnectionWrapper(@NonNull Context context,
|
||||||
|
@NonNull PeerConnectionFactory factory,
|
||||||
|
@NonNull PeerConnection.Observer observer,
|
||||||
|
@NonNull VideoSink localRenderer,
|
||||||
|
@NonNull List<PeerConnection.IceServer> turnServers,
|
||||||
|
@NonNull CameraEventListener cameraEventListener,
|
||||||
|
@NonNull EglBase eglBase,
|
||||||
|
boolean hideIp)
|
||||||
|
{
|
||||||
|
List<PeerConnection.IceServer> iceServers = new LinkedList<>();
|
||||||
|
iceServers.add(STUN_SERVER);
|
||||||
|
iceServers.addAll(turnServers);
|
||||||
|
|
||||||
|
MediaConstraints constraints = new MediaConstraints();
|
||||||
|
MediaConstraints audioConstraints = new MediaConstraints();
|
||||||
|
PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
|
||||||
|
|
||||||
|
configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
|
||||||
|
configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
|
||||||
|
|
||||||
|
if (hideIp) {
|
||||||
|
configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
||||||
|
audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
||||||
|
|
||||||
|
this.peerConnection = factory.createPeerConnection(configuration, constraints, observer);
|
||||||
|
this.peerConnection.setAudioPlayout(false);
|
||||||
|
this.peerConnection.setAudioRecording(false);
|
||||||
|
|
||||||
|
MediaStream mediaStream = factory.createLocalMediaStream("ARDAMS");
|
||||||
|
this.audioSource = factory.createAudioSource(audioConstraints);
|
||||||
|
this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
|
||||||
|
this.audioTrack.setEnabled(false);
|
||||||
|
mediaStream.addTrack(audioTrack);
|
||||||
|
|
||||||
|
this.camera = new Camera(context, cameraEventListener);
|
||||||
|
|
||||||
|
if (camera.capturer != null) {
|
||||||
|
this.videoSource = factory.createVideoSource(false);
|
||||||
|
this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
|
||||||
|
|
||||||
|
camera.capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", eglBase.getEglBaseContext()), context, videoSource.getCapturerObserver());
|
||||||
|
|
||||||
|
this.videoTrack.addSink(localRenderer);
|
||||||
|
this.videoTrack.setEnabled(false);
|
||||||
|
mediaStream.addTrack(videoTrack);
|
||||||
|
} else {
|
||||||
|
this.videoSource = null;
|
||||||
|
this.videoTrack = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.peerConnection.addStream(mediaStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoEnabled(boolean enabled) {
|
||||||
|
if (this.videoTrack != null) {
|
||||||
|
this.videoTrack.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
camera.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flipCamera() {
|
||||||
|
camera.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CameraState getCameraState() {
|
||||||
|
return new CameraState(camera.getActiveDirection(), camera.getCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommunicationMode() {
|
||||||
|
this.peerConnection.setAudioPlayout(true);
|
||||||
|
this.peerConnection.setAudioRecording(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioEnabled(boolean enabled) {
|
||||||
|
this.audioTrack.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataChannel createDataChannel(String name) {
|
||||||
|
DataChannel.Init dataChannelConfiguration = new DataChannel.Init();
|
||||||
|
dataChannelConfiguration.ordered = true;
|
||||||
|
|
||||||
|
return this.peerConnection.createDataChannel(name, dataChannelConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionDescription createOffer(MediaConstraints mediaConstraints) throws PeerConnectionException {
|
||||||
|
final SettableFuture<SessionDescription> future = new SettableFuture<>();
|
||||||
|
|
||||||
|
peerConnection.createOffer(new SdpObserver() {
|
||||||
|
@Override
|
||||||
|
public void onCreateSuccess(SessionDescription sdp) {
|
||||||
|
future.set(sdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateFailure(String error) {
|
||||||
|
future.setException(new PeerConnectionException(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetSuccess() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFailure(String error) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}, mediaConstraints);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return correctSessionDescription(future.get());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new PeerConnectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionDescription createAnswer(MediaConstraints mediaConstraints) throws PeerConnectionException {
|
||||||
|
final SettableFuture<SessionDescription> future = new SettableFuture<>();
|
||||||
|
|
||||||
|
peerConnection.createAnswer(new SdpObserver() {
|
||||||
|
@Override
|
||||||
|
public void onCreateSuccess(SessionDescription sdp) {
|
||||||
|
future.set(sdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateFailure(String error) {
|
||||||
|
future.setException(new PeerConnectionException(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetSuccess() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFailure(String error) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}, mediaConstraints);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return correctSessionDescription(future.get());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new PeerConnectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemoteDescription(SessionDescription sdp) throws PeerConnectionException {
|
||||||
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
||||||
|
|
||||||
|
peerConnection.setRemoteDescription(new SdpObserver() {
|
||||||
|
@Override
|
||||||
|
public void onCreateSuccess(SessionDescription sdp) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateFailure(String error) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetSuccess() {
|
||||||
|
future.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFailure(String error) {
|
||||||
|
future.setException(new PeerConnectionException(error));
|
||||||
|
}
|
||||||
|
}, sdp);
|
||||||
|
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new PeerConnectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalDescription(SessionDescription sdp) throws PeerConnectionException {
|
||||||
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
||||||
|
|
||||||
|
peerConnection.setLocalDescription(new SdpObserver() {
|
||||||
|
@Override
|
||||||
|
public void onCreateSuccess(SessionDescription sdp) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateFailure(String error) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetSuccess() {
|
||||||
|
future.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetFailure(String error) {
|
||||||
|
future.setException(new PeerConnectionException(error));
|
||||||
|
}
|
||||||
|
}, sdp);
|
||||||
|
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new PeerConnectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
this.camera.dispose();
|
||||||
|
|
||||||
|
if (this.videoSource != null) {
|
||||||
|
this.videoSource.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.audioSource.dispose();
|
||||||
|
this.peerConnection.close();
|
||||||
|
this.peerConnection.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addIceCandidate(IceCandidate candidate) {
|
||||||
|
return this.peerConnection.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private SessionDescription correctSessionDescription(SessionDescription sessionDescription) {
|
||||||
|
String updatedSdp = sessionDescription.description.replaceAll("(a=fmtp:111 ((?!cbr=).)*)\r?\n", "$1;cbr=1\r\n");
|
||||||
|
updatedSdp = updatedSdp.replaceAll(".+urn:ietf:params:rtp-hdrext:ssrc-audio-level.*\r?\n", "");
|
||||||
|
|
||||||
|
return new SessionDescription(sessionDescription.type, updatedSdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PeerConnectionException extends Exception {
|
||||||
|
public PeerConnectionException(String error) {
|
||||||
|
super(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerConnectionException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Camera implements CameraVideoCapturer.CameraSwitchHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final CameraVideoCapturer capturer;
|
||||||
|
private final CameraEventListener cameraEventListener;
|
||||||
|
private final int cameraCount;
|
||||||
|
|
||||||
|
private CameraState.Direction activeDirection;
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
Camera(@NonNull Context context, @NonNull CameraEventListener cameraEventListener)
|
||||||
|
{
|
||||||
|
this.cameraEventListener = cameraEventListener;
|
||||||
|
CameraEnumerator enumerator = getCameraEnumerator(context);
|
||||||
|
cameraCount = enumerator.getDeviceNames().length;
|
||||||
|
|
||||||
|
CameraVideoCapturer capturerCandidate = createVideoCapturer(enumerator, FRONT);
|
||||||
|
if (capturerCandidate != null) {
|
||||||
|
activeDirection = FRONT;
|
||||||
|
} else {
|
||||||
|
capturerCandidate = createVideoCapturer(enumerator, BACK);
|
||||||
|
if (capturerCandidate != null) {
|
||||||
|
activeDirection = BACK;
|
||||||
|
} else {
|
||||||
|
activeDirection = NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capturer = capturerCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flip() {
|
||||||
|
if (capturer == null || cameraCount < 2) {
|
||||||
|
Log.w(TAG, "Tried to flip the camera, but we only have " + cameraCount + " of them.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activeDirection = PENDING;
|
||||||
|
capturer.switchCamera(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
|
||||||
|
if (capturer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (enabled) {
|
||||||
|
capturer.startCapture(1280, 720, 30);
|
||||||
|
} else {
|
||||||
|
capturer.stopCapture();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.w(TAG, "Got interrupted while trying to stop video capture", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
if (capturer != null) {
|
||||||
|
capturer.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return cameraCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull CameraState.Direction getActiveDirection() {
|
||||||
|
return enabled ? activeDirection : NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable CameraVideoCapturer getCapturer() {
|
||||||
|
return capturer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull CameraEnumerator enumerator,
|
||||||
|
@NonNull CameraState.Direction direction)
|
||||||
|
{
|
||||||
|
String[] deviceNames = enumerator.getDeviceNames();
|
||||||
|
for (String deviceName : deviceNames) {
|
||||||
|
if ((direction == FRONT && enumerator.isFrontFacing(deviceName)) ||
|
||||||
|
(direction == BACK && enumerator.isBackFacing(deviceName)))
|
||||||
|
{
|
||||||
|
return enumerator.createCapturer(deviceName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull CameraEnumerator getCameraEnumerator(@NonNull Context context) {
|
||||||
|
boolean camera2EnumeratorIsSupported = false;
|
||||||
|
try {
|
||||||
|
camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
|
||||||
|
} catch (final Throwable throwable) {
|
||||||
|
Log.w(TAG, "Camera2Enumator.isSupport() threw.", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
|
||||||
|
|
||||||
|
return camera2EnumeratorIsSupported ? new Camera2Enumerator(context)
|
||||||
|
: new Camera1Enumerator(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraSwitchDone(boolean isFrontFacing) {
|
||||||
|
activeDirection = isFrontFacing ? FRONT : BACK;
|
||||||
|
cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraSwitchError(String errorMessage) {
|
||||||
|
Log.e(TAG, "onCameraSwitchError: " + errorMessage);
|
||||||
|
cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CameraEventListener {
|
||||||
|
void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
|
||||||
|
}
|
||||||
|
}
|
2248
src/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java
Normal file
2248
src/org/thoughtcrime/securesms/webrtc/WebRtcDataProtos.java
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue