diff --git a/res/drawable/webrtc_camera_front_button.xml b/res/drawable/webrtc_camera_front_button.xml
new file mode 100644
index 000000000..cda55648d
--- /dev/null
+++ b/res/drawable/webrtc_camera_front_button.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/webrtc_camera_flip_button.xml b/res/drawable/webrtc_camera_rear_button.xml
similarity index 78%
rename from res/drawable/webrtc_camera_flip_button.xml
rename to res/drawable/webrtc_camera_rear_button.xml
index e422c8295..697a1b4e5 100644
--- a/res/drawable/webrtc_camera_flip_button.xml
+++ b/res/drawable/webrtc_camera_rear_button.xml
@@ -1,6 +1,5 @@
-
+ android:background="@drawable/webrtc_camera_rear_button"
+ android:visibility="gone"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ab10363c5..c298f1454 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -995,7 +995,7 @@
Signal Call
Mute
- Use rear camera
+ Switch Cameras
Signal Call
diff --git a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
index 9c9b0989e..096b0ff4c 100644
--- a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
+++ b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
@@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.webrtc.CameraState;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SignalProtocolAddress;
@@ -160,10 +161,9 @@ public class WebRtcCallActivity extends Activity {
startService(intent);
}
- private void handleSetCameraFlip(boolean isRear) {
+ private void handleFlipCamera() {
Intent intent = new Intent(this, WebRtcCallService.class);
- intent.setAction(WebRtcCallService.ACTION_SET_CAMERA_FLIP);
- intent.putExtra(WebRtcCallService.EXTRA_CAMERA_FLIP_REAR, isRear);
+ intent.setAction(WebRtcCallService.ACTION_FLIP_CAMERA);
startService(intent);
}
@@ -330,10 +330,10 @@ public class WebRtcCallActivity extends Activity {
case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break;
}
- callScreen.setLocalVideoEnabled(event.isLocalVideoEnabled());
callScreen.setRemoteVideoEnabled(event.isRemoteVideoEnabled());
callScreen.updateAudioState(event.isBluetoothAvailable(), event.isMicrophoneEnabled());
callScreen.setControlsEnabled(event.getState() != WebRtcViewModel.State.CALL_INCOMING);
+ callScreen.setLocalVideoState(event.getLocalCameraState());
}
private class HangupButtonListener implements WebRtcCallScreen.HangupButtonListener {
@@ -358,7 +358,9 @@ public class WebRtcCallActivity extends Activity {
private class CameraFlipButtonListener implements WebRtcCallControls.CameraFlipButtonListener {
@Override
- public void onToggle(boolean isRear) { WebRtcCallActivity.this.handleSetCameraFlip(isRear); }
+ public void onToggle() {
+ WebRtcCallActivity.this.handleFlipCamera();
+ }
}
private class SpeakerButtonListener implements WebRtcCallControls.SpeakerButtonListener {
diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java
index a7d800527..7b091662b 100644
--- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java
+++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java
@@ -5,8 +5,8 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AccessibleToggleButton;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.webrtc.CameraState;
public class WebRtcCallControls extends LinearLayout {
@@ -27,9 +28,10 @@ public class WebRtcCallControls extends LinearLayout {
private AccessibleToggleButton audioMuteButton;
private AccessibleToggleButton videoMuteButton;
- private AccessibleToggleButton cameraFlipButton;
private AccessibleToggleButton speakerButton;
private AccessibleToggleButton bluetoothButton;
+ private AccessibleToggleButton cameraFlipButton;
+ private boolean cameraFlipAvailable;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@@ -62,7 +64,6 @@ public class WebRtcCallControls extends LinearLayout {
this.audioMuteButton = ViewUtil.findById(this, R.id.muteButton);
this.videoMuteButton = ViewUtil.findById(this, R.id.video_mute_button);
this.cameraFlipButton = ViewUtil.findById(this, R.id.camera_flip_button);
- this.cameraFlipButton.setVisibility(View.INVISIBLE); // shown once video is enabled
}
public void setAudioMuteButtonListener(final MuteButtonListener listener) {
@@ -80,7 +81,7 @@ public class WebRtcCallControls extends LinearLayout {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
boolean videoMuted = !isChecked;
listener.onToggle(videoMuted);
- cameraFlipButton.setVisibility(videoMuted ? View.INVISIBLE : View.VISIBLE);
+ cameraFlipButton.setVisibility(!videoMuted && cameraFlipAvailable ? View.VISIBLE : View.GONE);
}
});
}
@@ -89,7 +90,10 @@ public class WebRtcCallControls extends LinearLayout {
cameraFlipButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- listener.onToggle(isChecked);
+ listener.onToggle();
+ cameraFlipButton.setBackgroundResource(isChecked ? R.drawable.webrtc_camera_front_button
+ : R.drawable.webrtc_camera_rear_button);
+ cameraFlipButton.setEnabled(false);
}
});
}
@@ -143,40 +147,46 @@ public class WebRtcCallControls extends LinearLayout {
videoMuteButton.setChecked(enabled, false);
}
+ public void setVideoAvailable(boolean available) {
+ videoMuteButton.setVisibility(available ? VISIBLE : GONE);
+ }
+
+ public void setCameraFlipButtonEnabled(boolean enabled) {
+ cameraFlipButton.setChecked(enabled, false);
+ }
+
+ public void setCameraFlipAvailable(boolean available) {
+ cameraFlipAvailable = available;
+ }
+
+ public void setCameraFlipClickable(boolean clickable) {
+ setControlEnabled(cameraFlipButton, clickable);
+ }
+
public void setMicrophoneEnabled(boolean enabled) {
audioMuteButton.setChecked(!enabled, false);
}
public void setControlsEnabled(boolean enabled) {
- if (enabled && Build.VERSION.SDK_INT >= 11) {
- speakerButton.setAlpha(1.0f);
- bluetoothButton.setAlpha(1.0f);
- videoMuteButton.setAlpha(1.0f);
- cameraFlipButton.setAlpha(1.0f);
- audioMuteButton.setAlpha(1.0f);
+ setControlEnabled(speakerButton, enabled);
+ setControlEnabled(bluetoothButton, enabled);
+ setControlEnabled(videoMuteButton, enabled);
+ setControlEnabled(cameraFlipButton, enabled);
+ setControlEnabled(audioMuteButton, enabled);
+ }
- speakerButton.setEnabled(true);
- bluetoothButton.setEnabled(true);
- videoMuteButton.setEnabled(true);
- cameraFlipButton.setEnabled(true);
- audioMuteButton.setEnabled(true);
- } else if (!enabled && Build.VERSION.SDK_INT >= 11) {
- speakerButton.setAlpha(0.3f);
- bluetoothButton.setAlpha(0.3f);
- videoMuteButton.setAlpha(0.3f);
- cameraFlipButton.setAlpha(0.3f);
- audioMuteButton.setAlpha(0.3f);
-
- speakerButton.setEnabled(false);
- bluetoothButton.setEnabled(false);
- videoMuteButton.setEnabled(false);
- cameraFlipButton.setEnabled(false);
- audioMuteButton.setEnabled(false);
+ private void setControlEnabled(@NonNull View view, boolean enabled) {
+ if (enabled) {
+ view.setAlpha(1.0f);
+ view.setEnabled(true);
+ } else {
+ view.setAlpha(0.3f);
+ view.setEnabled(false);
}
}
public void displayVideoTooltip(ViewGroup viewGroup) {
- if (Build.VERSION.SDK_INT > 15) {
+ if (Build.VERSION.SDK_INT > 15 && videoMuteButton.getVisibility() == VISIBLE) {
final ToolTipsManager toolTipsManager = new ToolTipsManager();
ToolTip toolTip = new ToolTip.Builder(getContext(), videoMuteButton, viewGroup,
@@ -184,12 +194,7 @@ public class WebRtcCallControls extends LinearLayout {
ToolTip.POSITION_BELOW).build();
toolTipsManager.show(toolTip);
- videoMuteButton.postDelayed(new Runnable() {
- @Override
- public void run() {
- toolTipsManager.findAndDismiss(videoMuteButton);
- }
- }, 4000);
+ videoMuteButton.postDelayed(() -> toolTipsManager.findAndDismiss(videoMuteButton), 4000);
}
}
@@ -198,7 +203,7 @@ public class WebRtcCallControls extends LinearLayout {
}
public static interface CameraFlipButtonListener {
- public void onToggle(boolean isRear);
+ public void onToggle();
}
public static interface SpeakerButtonListener {
diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
index 30d176563..7db63b4a8 100644
--- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
+++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
@@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.webrtc.CameraState;
import org.webrtc.SurfaceViewRenderer;
import org.whispersystems.libsignal.IdentityKey;
@@ -62,6 +63,7 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
private static final String TAG = WebRtcCallScreen.class.getSimpleName();
private ImageView photo;
+ private SurfaceViewRenderer localRenderer;
private PercentFrameLayout localRenderLayout;
private PercentFrameLayout remoteRenderLayout;
private TextView name;
@@ -187,14 +189,19 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
this.controls.setControlsEnabled(enabled);
}
- public void setLocalVideoEnabled(boolean enabled) {
- if (enabled && this.localRenderLayout.isHidden()) {
- this.controls.setVideoEnabled(true);
- this.localRenderLayout.setHidden(false);
- this.localRenderLayout.requestLayout();
- } else if (!enabled && !this.localRenderLayout.isHidden()){
- this.controls.setVideoEnabled(false);
- this.localRenderLayout.setHidden(true);
+ public void setLocalVideoState(@NonNull CameraState cameraState) {
+ this.controls.setVideoAvailable(cameraState.getCameraCount() > 0);
+ this.controls.setVideoEnabled(cameraState.isEnabled());
+ this.controls.setCameraFlipAvailable(cameraState.getCameraCount() > 1);
+ this.controls.setCameraFlipClickable(cameraState.getActiveDirection() != CameraState.Direction.PENDING);
+ this.controls.setCameraFlipButtonEnabled(cameraState.getActiveDirection() == CameraState.Direction.BACK);
+
+ if (this.localRenderer != null) {
+ this.localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT);
+ }
+
+ if (this.localRenderLayout.isHidden() == cameraState.isEnabled()) {
+ this.localRenderLayout.setHidden(!cameraState.isEnabled());
this.localRenderLayout.requestLayout();
}
}
@@ -276,6 +283,8 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
localRenderLayout.addView(localRenderer);
remoteRenderLayout.addView(remoteRenderer);
+
+ this.localRenderer = localRenderer;
}
}
diff --git a/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java
index 325a6730e..aeeb78c74 100644
--- a/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java
+++ b/src/org/thoughtcrime/securesms/events/WebRtcViewModel.java
@@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.webrtc.CameraState;
import org.whispersystems.libsignal.IdentityKey;
public class WebRtcViewModel {
@@ -30,29 +31,40 @@ public class WebRtcViewModel {
private final @Nullable IdentityKey identityKey;
private final boolean remoteVideoEnabled;
- private final boolean localVideoEnabled;
private final boolean isBluetoothAvailable;
private final boolean isMicrophoneEnabled;
- public WebRtcViewModel(@NonNull State state, @NonNull Recipient recipient,
- boolean localVideoEnabled, boolean remoteVideoEnabled,
- boolean isBluetoothAvailable, boolean isMicrophoneEnabled)
+ private final CameraState localCameraState;
+
+ public WebRtcViewModel(@NonNull State state,
+ @NonNull Recipient recipient,
+ @NonNull CameraState localCameraState,
+ boolean remoteVideoEnabled,
+ boolean isBluetoothAvailable,
+ boolean isMicrophoneEnabled)
{
- this(state, recipient, null,
- localVideoEnabled, remoteVideoEnabled,
- isBluetoothAvailable, isMicrophoneEnabled);
+ this(state,
+ recipient,
+ null,
+ localCameraState,
+ remoteVideoEnabled,
+ isBluetoothAvailable,
+ isMicrophoneEnabled);
}
- public WebRtcViewModel(@NonNull State state, @NonNull Recipient recipient,
+ public WebRtcViewModel(@NonNull State state,
+ @NonNull Recipient recipient,
@Nullable IdentityKey identityKey,
- boolean localVideoEnabled, boolean remoteVideoEnabled,
- boolean isBluetoothAvailable, boolean isMicrophoneEnabled)
+ @NonNull CameraState localCameraState,
+ boolean remoteVideoEnabled,
+ boolean isBluetoothAvailable,
+ boolean isMicrophoneEnabled)
{
this.state = state;
this.recipient = recipient;
+ this.localCameraState = localCameraState;
this.identityKey = identityKey;
- this.localVideoEnabled = localVideoEnabled;
this.remoteVideoEnabled = remoteVideoEnabled;
this.isBluetoothAvailable = isBluetoothAvailable;
this.isMicrophoneEnabled = isMicrophoneEnabled;
@@ -66,8 +78,11 @@ public class WebRtcViewModel {
return recipient;
}
- @Nullable
- public IdentityKey getIdentityKey() {
+ public @NonNull CameraState getLocalCameraState() {
+ return localCameraState;
+ }
+
+ public @Nullable IdentityKey getIdentityKey() {
return identityKey;
}
@@ -75,10 +90,6 @@ public class WebRtcViewModel {
return remoteVideoEnabled;
}
- public boolean isLocalVideoEnabled() {
- return localVideoEnabled;
- }
-
public boolean isBluetoothAvailable() {
return isBluetoothAvailable;
}
@@ -87,7 +98,8 @@ public class WebRtcViewModel {
return isMicrophoneEnabled;
}
+
public String toString() {
- return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localVideoEnabled + "]";
+ return "[State: " + state + ", recipient: " + recipient.getAddress() + ", identity: " + identityKey + ", remoteVideo: " + remoteVideoEnabled + ", localVideo: " + localCameraState.isEnabled() + "]";
}
}
diff --git a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
index 58aba5f92..81c428412 100644
--- a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
+++ b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
@@ -30,7 +30,6 @@ import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
@@ -43,6 +42,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder;
+import org.thoughtcrime.securesms.webrtc.CameraState;
import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver;
import org.thoughtcrime.securesms.webrtc.PeerConnectionFactoryOptions;
import org.thoughtcrime.securesms.webrtc.PeerConnectionWrapper;
@@ -104,7 +104,12 @@ import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INC
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_RINGING;
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_OUTGOING_RINGING;
-public class WebRtcCallService extends Service implements InjectableType, PeerConnection.Observer, DataChannel.Observer, BluetoothStateManager.BluetoothStateListener {
+public class WebRtcCallService extends Service implements InjectableType,
+ PeerConnection.Observer,
+ DataChannel.Observer,
+ BluetoothStateManager.BluetoothStateListener,
+ PeerConnectionWrapper.CameraEventListener
+{
private static final String TAG = WebRtcCallService.class.getSimpleName();
@@ -116,7 +121,6 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
public static final String EXTRA_REMOTE_ADDRESS = "remote_address";
public static final String EXTRA_MUTE = "mute_value";
- public static final String EXTRA_CAMERA_FLIP_REAR = "camera_flip_rear_value";
public static final String EXTRA_AVAILABLE = "enabled_value";
public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description";
public static final String EXTRA_TIMESTAMP = "timestamp";
@@ -133,7 +137,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP";
public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO";
public static final String ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO";
- public static final String ACTION_SET_CAMERA_FLIP = "SET_CAMERA_FLIP";
+ public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA";
public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE";
public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE";
public static final String ACTION_SCREEN_OFF = "SCREEN_OFF";
@@ -149,11 +153,11 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
public static final String ACTION_REMOTE_VIDEO_MUTE = "REMOTE_VIDEO_MUTE";
public static final String ACTION_ICE_CONNECTED = "ICE_CONNECTED";
- private CallState callState = CallState.STATE_IDLE;
- private boolean microphoneEnabled = true;
- private boolean localVideoEnabled = false;
- private boolean remoteVideoEnabled = false;
- private boolean bluetoothAvailable = false;
+ private CallState callState = CallState.STATE_IDLE;
+ private CameraState localCameraState = CameraState.UNKNOWN;
+ private boolean microphoneEnabled = true;
+ private boolean remoteVideoEnabled = false;
+ private boolean bluetoothAvailable = false;
@Inject public SignalServiceMessageSender messageSender;
@Inject public SignalServiceAccountManager accountManager;
@@ -210,7 +214,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
else if (intent.getAction().equals(ACTION_REMOTE_HANGUP)) handleRemoteHangup(intent);
else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent);
else if (intent.getAction().equals(ACTION_SET_MUTE_VIDEO)) handleSetMuteVideo(intent);
- else if (intent.getAction().equals(ACTION_SET_CAMERA_FLIP)) handleSetCameraFlip(intent);
+ else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent);
else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent);
else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent);
else if (intent.getAction().equals((ACTION_SCREEN_OFF))) handleScreenOffChange(intent);
@@ -265,6 +269,15 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
startService(intent);
}
+ @Override
+ public void onCameraSwitchCompleted(@NonNull CameraState newCameraState) {
+ this.localCameraState = newCameraState;
+ if (recipient != null) {
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ }
+ }
+
+
// Initializers
private void initializeResources() {
@@ -358,7 +371,8 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this);
- WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, !isSystemContact || isAlwaysTurn);
+ WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, !isSystemContact || isAlwaysTurn);
+ WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState();
WebRtcCallService.this.peerConnection.setRemoteDescription(new SessionDescription(SessionDescription.Type.OFFER, offer));
WebRtcCallService.this.lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
@@ -379,6 +393,10 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
terminate();
}
});
+
+ if (recipient != null) {
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ }
} catch (PeerConnectionException e) {
Log.w(TAG, e);
terminate();
@@ -400,7 +418,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
initializeVideo();
- sendMessage(WebRtcViewModel.State.CALL_OUTGOING, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_OUTGOING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
audioManager.initializeAudioForCall();
audioManager.startOutgoingRinger(OutgoingRinger.Type.SONAR);
@@ -417,7 +435,8 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
try {
boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this);
- WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, isAlwaysTurn);
+ WebRtcCallService.this.peerConnection = new PeerConnectionWrapper(WebRtcCallService.this, peerConnectionFactory, WebRtcCallService.this, localRenderer, result, WebRtcCallService.this, isAlwaysTurn);
+ WebRtcCallService.this.localCameraState = WebRtcCallService.this.peerConnection.getCameraState();
WebRtcCallService.this.dataChannel = WebRtcCallService.this.peerConnection.createDataChannel(DATA_CHANNEL_NAME);
WebRtcCallService.this.dataChannel.registerObserver(WebRtcCallService.this);
@@ -434,16 +453,20 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
Log.w(TAG, error);
if (error instanceof UntrustedIdentityException) {
- sendMessage(WebRtcViewModel.State.UNTRUSTED_IDENTITY, recipient, ((UntrustedIdentityException)error).getIdentityKey(), localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.UNTRUSTED_IDENTITY, recipient, ((UntrustedIdentityException)error).getIdentityKey(), localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
} else if (error instanceof UnregisteredUserException) {
- sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.NO_SUCH_USER, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
} else if (error instanceof IOException) {
- sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
terminate();
}
});
+
+ if (recipient != null) {
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ }
} catch (PeerConnectionException e) {
Log.w(TAG, e);
terminate();
@@ -475,7 +498,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
@Override
public void onFailureContinue(Throwable error) {
Log.w(TAG, error);
- sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
terminate();
}
@@ -529,7 +552,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
@Override
public void onFailureContinue(Throwable error) {
Log.w(TAG, error);
- sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
terminate();
}
@@ -543,7 +566,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.callState = CallState.STATE_LOCAL_RINGING;
this.lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
- sendMessage(WebRtcViewModel.State.CALL_INCOMING, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_INCOMING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
startCallCardActivity();
audioManager.initializeAudioForCall();
@@ -565,7 +588,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.callState = CallState.STATE_REMOTE_RINGING;
this.audioManager.startOutgoingRinger(OutgoingRinger.Type.RINGING);
- sendMessage(WebRtcViewModel.State.CALL_RINGING, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_RINGING, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
}
@@ -589,10 +612,10 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
callState = CallState.STATE_CONNECTED;
- if (localVideoEnabled) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
- else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
+ if (localCameraState.isEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
+ else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
- sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_CONNECTED, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
unregisterPowerButtonReceiver();
@@ -600,12 +623,12 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.peerConnection.setCommunicationMode();
this.peerConnection.setAudioEnabled(microphoneEnabled);
- this.peerConnection.setVideoEnabled(localVideoEnabled);
+ this.peerConnection.setVideoEnabled(localCameraState.isEnabled());
this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder()
.setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder()
.setId(this.callId)
- .setEnabled(localVideoEnabled))
+ .setEnabled(localCameraState.isEnabled()))
.build().toByteArray()), false));
}
@@ -644,7 +667,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
return;
}
- sendMessage(WebRtcViewModel.State.CALL_BUSY, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_BUSY, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
audioManager.startOutgoingRinger(OutgoingRinger.Type.BUSY);
Util.runOnMainDelayed(new Runnable() {
@@ -666,7 +689,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.callState != CallState.STATE_CONNECTED)
{
Log.w(TAG, "Timing out call: " + this.callId);
- sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) {
insertMissedCall(this.recipient, true);
@@ -735,7 +758,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder().setHangup(Hangup.newBuilder().setId(this.callId)).build().toByteArray()), false));
sendMessage(this.recipient, SignalServiceCallMessage.forHangup(new HangupMessage(this.callId)));
- sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
terminate();
@@ -752,9 +775,9 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
}
if (this.callState == CallState.STATE_DIALING || this.callState == CallState.STATE_REMOTE_RINGING) {
- sendMessage(WebRtcViewModel.State.RECIPIENT_UNAVAILABLE, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.RECIPIENT_UNAVAILABLE, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
} else {
- sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
if (this.callState == CallState.STATE_ANSWERING || this.callState == CallState.STATE_LOCAL_RINGING) {
@@ -777,26 +800,25 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
AudioManager audioManager = ServiceUtil.getAudioManager(this);
boolean muted = intent.getBooleanExtra(EXTRA_MUTE, false);
- this.localVideoEnabled = !muted;
-
if (this.peerConnection != null) {
- this.peerConnection.setVideoEnabled(this.localVideoEnabled);
+ this.peerConnection.setVideoEnabled(!muted);
+ this.localCameraState = this.peerConnection.getCameraState();
}
if (this.callId != null && this.dataChannel != null) {
this.dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(Data.newBuilder()
.setVideoStreamingStatus(WebRtcDataProtos.VideoStreamingStatus.newBuilder()
.setId(this.callId)
- .setEnabled(localVideoEnabled))
+ .setEnabled(!muted))
.build().toByteArray()), false));
}
if (callState == CallState.STATE_CONNECTED) {
- if (localVideoEnabled) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
- else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
+ if (localCameraState.isEnabled()) this.lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO);
+ else this.lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
}
- if (localVideoEnabled &&
+ if (localCameraState.isEnabled() &&
!audioManager.isSpeakerphoneOn() &&
!audioManager.isBluetoothScoOn() &&
!audioManager.isWiredHeadsetOn())
@@ -804,16 +826,17 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
audioManager.setSpeakerphoneOn(true);
}
- sendMessage(viewModelStateFor(callState), this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(viewModelStateFor(callState), this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
private void handleSetCameraFlip(Intent intent) {
- boolean isRear = intent.getBooleanExtra(EXTRA_CAMERA_FLIP_REAR, false);
- Log.w(TAG, "handleSetCameraFlip(isRear=" + isRear + ")...");
+ Log.w(TAG, "handleSetCameraFlip()...");
- if (this.localVideoEnabled) {
- if (this.peerConnection != null) {
- this.peerConnection.flipCameras(isRear);
+ if (localCameraState.isEnabled() && peerConnection != null) {
+ peerConnection.flipCamera();
+ localCameraState = peerConnection.getCameraState();
+ if (recipient != null) {
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
}
}
@@ -822,7 +845,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false);
if (recipient != null) {
- sendMessage(viewModelStateFor(callState), recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
}
@@ -839,12 +862,12 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
if (present && audioManager.isSpeakerphoneOn()) {
audioManager.setSpeakerphoneOn(false);
audioManager.setBluetoothScoOn(false);
- } else if (!present && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && localVideoEnabled) {
+ } else if (!present && !audioManager.isSpeakerphoneOn() && !audioManager.isBluetoothScoOn() && localCameraState.isEnabled()) {
audioManager.setSpeakerphoneOn(true);
}
if (recipient != null) {
- sendMessage(viewModelStateFor(callState), recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(viewModelStateFor(callState), recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
}
}
@@ -868,7 +891,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
}
this.remoteVideoEnabled = !muted;
- sendMessage(WebRtcViewModel.State.CALL_CONNECTED, this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
+ sendMessage(WebRtcViewModel.State.CALL_CONNECTED, this.recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
/// Helper Methods
@@ -932,10 +955,10 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
}
this.callState = CallState.STATE_IDLE;
+ this.localCameraState = CameraState.UNKNOWN;
this.recipient = null;
this.callId = null;
this.microphoneEnabled = true;
- this.localVideoEnabled = false;
this.remoteVideoEnabled = false;
this.pendingOutgoingIceUpdates = null;
this.pendingIncomingIceUpdates = null;
@@ -944,20 +967,24 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
private void sendMessage(@NonNull WebRtcViewModel.State state,
- @NonNull Recipient recipient,
- boolean localVideoEnabled, boolean remoteVideoEnabled,
- boolean bluetoothAvailable, boolean microphoneEnabled)
+ @NonNull Recipient recipient,
+ @NonNull CameraState localCameraState,
+ boolean remoteVideoEnabled,
+ boolean bluetoothAvailable,
+ boolean microphoneEnabled)
{
- EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled));
+ EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled));
}
private void sendMessage(@NonNull WebRtcViewModel.State state,
- @NonNull Recipient recipient,
- @NonNull IdentityKey identityKey,
- boolean localVideoEnabled, boolean remoteVideoEnabled,
- boolean bluetoothAvailable, boolean microphoneEnabled)
+ @NonNull Recipient recipient,
+ @NonNull IdentityKey identityKey,
+ @NonNull CameraState localCameraState,
+ boolean remoteVideoEnabled,
+ boolean bluetoothAvailable,
+ boolean microphoneEnabled)
{
- EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, identityKey, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled));
+ EventBus.getDefault().postSticky(new WebRtcViewModel(state, recipient, identityKey, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled));
}
private ListenableFutureTask sendMessage(@NonNull final Recipient recipient,
diff --git a/src/org/thoughtcrime/securesms/webrtc/CameraState.java b/src/org/thoughtcrime/securesms/webrtc/CameraState.java
new file mode 100644
index 000000000..c5b0e2572
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/webrtc/CameraState.java
@@ -0,0 +1,32 @@
+package org.thoughtcrime.securesms.webrtc;
+
+import android.support.annotation.NonNull;
+
+public class CameraState {
+
+ public static final CameraState UNKNOWN = new CameraState(Direction.NONE, 0);
+
+ private final Direction activeDirection;
+ private final int cameraCount;
+
+ public CameraState(@NonNull Direction activeDirection, int cameraCount) {
+ this.activeDirection = activeDirection;
+ this.cameraCount = cameraCount;
+ }
+
+ public int getCameraCount() {
+ return cameraCount;
+ }
+
+ public Direction getActiveDirection() {
+ return activeDirection;
+ }
+
+ public boolean isEnabled() {
+ return this.activeDirection != Direction.NONE;
+ }
+
+ public enum Direction {
+ FRONT, BACK, NONE, PENDING
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java b/src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java
index 0e63b28c1..f48c39990 100644
--- a/src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java
+++ b/src/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.java
@@ -21,7 +21,6 @@ import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
-import org.webrtc.VideoCapturer;
import org.webrtc.VideoRenderer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
@@ -30,6 +29,11 @@ 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();
@@ -38,25 +42,17 @@ public class PeerConnectionWrapper {
@NonNull private final PeerConnection peerConnection;
@NonNull private final AudioTrack audioTrack;
@NonNull private final AudioSource audioSource;
-
- @Nullable private final VideoCapturer videoCapturer;
- @Nullable private final VideoCapturer videoCapturerRear;
+ @NonNull private final Camera camera;
@Nullable private final VideoSource videoSource;
- @Nullable private final VideoSource videoSourceRear;
@Nullable private final VideoTrack videoTrack;
- @Nullable private final VideoTrack videoTrackRear;
- @Nullable private VideoCapturer videoCapturerActive;
- @Nullable private VideoTrack videoTrackActive;
-
- @Nullable private final MediaStream mediaStream;
-
- public PeerConnectionWrapper(@NonNull Context context,
- @NonNull PeerConnectionFactory factory,
- @NonNull PeerConnection.Observer observer,
- @NonNull VideoRenderer.Callbacks localRenderer,
+ public PeerConnectionWrapper(@NonNull Context context,
+ @NonNull PeerConnectionFactory factory,
+ @NonNull PeerConnection.Observer observer,
+ @NonNull VideoRenderer.Callbacks localRenderer,
@NonNull List turnServers,
- boolean hideIp)
+ @NonNull CameraEventListener cameraEventListener,
+ boolean hideIp)
{
List iceServers = new LinkedList<>();
iceServers.add(STUN_SERVER);
@@ -80,69 +76,42 @@ public class PeerConnectionWrapper {
this.peerConnection.setAudioPlayout(false);
this.peerConnection.setAudioRecording(false);
- this.videoCapturer = createVideoCapturer(context, false);
- this.videoCapturerRear = createVideoCapturer(context, true);
-
- this.videoCapturerActive = videoCapturer;
-
- this.mediaStream = factory.createLocalMediaStream("ARDAMS");
+ MediaStream mediaStream = factory.createLocalMediaStream("ARDAMS");
this.audioSource = factory.createAudioSource(audioConstraints);
this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
this.audioTrack.setEnabled(false);
- this.mediaStream.addTrack(audioTrack);
+ mediaStream.addTrack(audioTrack);
- if (videoCapturer != null) {
- this.videoSource = factory.createVideoSource(videoCapturer);
+ this.camera = new Camera(context, cameraEventListener);
+
+ if (camera.capturer != null) {
+ this.videoSource = factory.createVideoSource(camera.capturer);
this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
- this.videoTrackActive = videoTrack;
-
this.videoTrack.addRenderer(new VideoRenderer(localRenderer));
this.videoTrack.setEnabled(false);
- this.mediaStream.addTrack(videoTrack);
+ mediaStream.addTrack(videoTrack);
} else {
this.videoSource = null;
this.videoTrack = null;
- this.videoTrackActive = null;
- }
-
- if (videoCapturerRear != null) {
- this.videoSourceRear = factory.createVideoSource(videoCapturerRear);
- this.videoTrackRear = factory.createVideoTrack("ARDAMSv0", videoSourceRear);
- this.videoTrackRear.addRenderer(new VideoRenderer(localRenderer));
- this.videoTrackRear.setEnabled(false);
- } else {
- this.videoSourceRear = null;
- this.videoTrackRear = null;
}
this.peerConnection.addStream(mediaStream);
}
public void setVideoEnabled(boolean enabled) {
- if (this.videoTrackActive != null) {
- this.videoTrackActive.setEnabled(enabled);
- }
-
- if (this.videoCapturerActive != null) {
- try {
- if (enabled) this.videoCapturerActive.startCapture(1280, 720, 30);
- else this.videoCapturerActive.stopCapture();
- } catch (InterruptedException e) {
- Log.w(TAG, e);
- }
+ if (this.videoTrack != null) {
+ this.videoTrack.setEnabled(enabled);
}
+ camera.setEnabled(enabled);
}
- public void flipCameras(boolean isRear) {
- if (videoCapturerRear != null) {
- setVideoEnabled(false);
- mediaStream.removeTrack(videoTrackActive);
- this.videoTrackActive = isRear ? videoTrackRear : videoTrack;
- this.videoCapturerActive = isRear ? videoCapturerRear : videoCapturer;
- mediaStream.addTrack(videoTrackActive);
- setVideoEnabled(true);
- }
+ public void flipCamera() {
+ camera.flip();
+ }
+
+ public CameraState getCameraState() {
+ return new CameraState(camera.getActiveDirection(), camera.getCount());
}
public void setCommunicationMode() {
@@ -294,32 +263,12 @@ public class PeerConnectionWrapper {
}
public void dispose() {
- if (this.videoCapturer != null) {
- try {
- this.videoCapturer.stopCapture();
- } catch (InterruptedException e) {
- Log.w(TAG, e);
- }
- this.videoCapturer.dispose();
- }
-
- if (this.videoCapturerRear != null) {
- try {
- this.videoCapturerRear.stopCapture();
- } catch (InterruptedException e) {
- Log.w(TAG, e);
- }
- this.videoCapturerRear.dispose();
- }
+ this.camera.dispose();
if (this.videoSource != null) {
this.videoSource.dispose();
}
- if (this.videoSourceRear != null) {
- this.videoSourceRear.dispose();
- }
-
this.audioSource.dispose();
this.peerConnection.close();
this.peerConnection.dispose();
@@ -329,57 +278,6 @@ public class PeerConnectionWrapper {
return this.peerConnection.addIceCandidate(candidate);
}
- private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull Context context, boolean rear) {
- boolean camera2EnumeratorIsSupported = false;
- try {
- camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
- } catch (final Throwable throwable) {
- Log.w(TAG, "Camera2Enumator.isSupport() threw.", throwable);
- }
-
- Log.w(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
- CameraEnumerator enumerator;
-
- if (camera2EnumeratorIsSupported) enumerator = new Camera2Enumerator(context);
- else enumerator = new Camera1Enumerator(true);
-
- String[] deviceNames = enumerator.getDeviceNames();
-
- for (String deviceName : deviceNames) {
- boolean isDesiredDirection =
- rear ? enumerator.isBackFacing(deviceName)
- : enumerator.isFrontFacing(deviceName);
- if (isDesiredDirection) {
- String direction = rear ? "rear" : "front";
- Log.w(TAG, "Creating " + direction + " facing camera capturer.");
- final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
-
- if (videoCapturer != null) {
- Log.w(TAG, "Found " + direction + " facing capturer: " + deviceName);
-
- return videoCapturer;
- }
- }
- }
-
- for (String deviceName : deviceNames) {
- boolean isDesiredDirection =
- rear ? enumerator.isBackFacing(deviceName)
- : enumerator.isFrontFacing(deviceName);
- if (!isDesiredDirection) {
- Log.w(TAG, "Creating other camera capturer.");
- final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
-
- if (videoCapturer != null) {
- Log.w(TAG, "Found other facing capturer: " + deviceName);
- return videoCapturer;
- }
- }
- }
-
- Log.w(TAG, "Video capture not supported!");
- return null;
- }
private SessionDescription correctSessionDescription(SessionDescription sessionDescription) {
String updatedSdp = sessionDescription.description.replaceAll("(a=fmtp:111 ((?!cbr=).)*)\r?\n", "$1;cbr=1\r\n");
@@ -397,4 +295,125 @@ public class PeerConnectionWrapper {
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.w(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);
+ }
}