mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Display audio levels for each participant in group calls.
This commit is contained in:
committed by
Cody Henthorne
parent
a9f208153c
commit
ec92d5ddb7
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.service.webrtc;
|
||||
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.LongSparseArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -11,13 +12,17 @@ import org.signal.core.util.logging.Log;
|
||||
import org.signal.ringrtc.CallException;
|
||||
import org.signal.ringrtc.GroupCall;
|
||||
import org.signal.ringrtc.PeekInfo;
|
||||
import org.thoughtcrime.securesms.events.CallParticipant;
|
||||
import org.thoughtcrime.securesms.events.CallParticipantId;
|
||||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.ringrtc.Camera;
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -104,6 +109,31 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull WebRtcEphemeralState handleGroupAudioLevelsChanged(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState) {
|
||||
GroupCall groupCall = currentState.getCallInfoState().requireGroupCall();
|
||||
LongSparseArray<GroupCall.RemoteDeviceState> remoteDeviceStates = groupCall.getRemoteDeviceStates();
|
||||
|
||||
CallParticipant.AudioLevel localAudioLevel = CallParticipant.AudioLevel.fromRawAudioLevel(groupCall.getLocalDeviceState().getAudioLevel());
|
||||
|
||||
HashMap<CallParticipantId, CallParticipant.AudioLevel> remoteAudioLevels = new HashMap<>();
|
||||
for (CallParticipant participant : currentState.getCallInfoState().getRemoteCallParticipants()) {
|
||||
CallParticipantId callParticipantId = participant.getCallParticipantId();
|
||||
|
||||
Integer audioLevel = null;
|
||||
if (remoteDeviceStates != null) {
|
||||
GroupCall.RemoteDeviceState state = remoteDeviceStates.get(callParticipantId.getDemuxId());
|
||||
if (state != null) {
|
||||
audioLevel = state.getAudioLevel();
|
||||
}
|
||||
}
|
||||
|
||||
remoteAudioLevels.put(callParticipantId, CallParticipant.AudioLevel.fromRawAudioLevel(audioLevel));
|
||||
}
|
||||
|
||||
return ephemeralState.copy(localAudioLevel, remoteAudioLevels);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull WebRtcServiceState handleGroupJoinedMembershipChanged(@NonNull WebRtcServiceState currentState) {
|
||||
Log.i(tag, "handleGroupJoinedMembershipChanged():");
|
||||
|
||||
@@ -45,7 +45,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor {
|
||||
GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId,
|
||||
SignalStore.internalValues().groupCallingServer(),
|
||||
new byte[0],
|
||||
null,
|
||||
AUDIO_LEVELS_INTERVAL,
|
||||
AudioProcessingMethodSelector.get(),
|
||||
webRtcInteractor.getGroupCallObserver());
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ public final class IncomingGroupCallActionProcessor extends DeviceAwareActionPro
|
||||
GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId,
|
||||
SignalStore.internalValues().groupCallingServer(),
|
||||
new byte[0],
|
||||
null,
|
||||
AUDIO_LEVELS_INTERVAL,
|
||||
AudioProcessingMethodSelector.get(),
|
||||
webRtcInteractor.getGroupCallObserver());
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -45,6 +46,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.ringrtc.CameraEventListener;
|
||||
import org.thoughtcrime.securesms.ringrtc.CameraState;
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
||||
import org.thoughtcrime.securesms.util.BubbleUtil;
|
||||
@@ -52,6 +54,7 @@ import org.thoughtcrime.securesms.util.NetworkUtil;
|
||||
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.rx.RxStore;
|
||||
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
|
||||
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
|
||||
import org.webrtc.PeerConnection;
|
||||
@@ -80,6 +83,10 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
import static org.thoughtcrime.securesms.events.WebRtcViewModel.GroupCallState.IDLE;
|
||||
import static org.thoughtcrime.securesms.events.WebRtcViewModel.State.CALL_INCOMING;
|
||||
import static org.thoughtcrime.securesms.events.WebRtcViewModel.State.NETWORK_FAILURE;
|
||||
@@ -106,16 +113,18 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
||||
private final Executor networkExecutor;
|
||||
private final LockManager lockManager;
|
||||
|
||||
private WebRtcServiceState serviceState;
|
||||
private boolean needsToSetSelfUuid = true;
|
||||
private WebRtcServiceState serviceState;
|
||||
private RxStore<WebRtcEphemeralState> ephemeralStateStore;
|
||||
private boolean needsToSetSelfUuid = true;
|
||||
|
||||
public SignalCallManager(@NonNull Application application) {
|
||||
this.context = application.getApplicationContext();
|
||||
this.messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||
this.accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
this.lockManager = new LockManager(this.context);
|
||||
this.serviceExecutor = Executors.newSingleThreadExecutor();
|
||||
this.networkExecutor = Executors.newSingleThreadExecutor();
|
||||
this.context = application.getApplicationContext();
|
||||
this.messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||
this.accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
this.lockManager = new LockManager(this.context);
|
||||
this.serviceExecutor = Executors.newSingleThreadExecutor();
|
||||
this.networkExecutor = Executors.newSingleThreadExecutor();
|
||||
this.ephemeralStateStore = new RxStore<>(new WebRtcEphemeralState(), Schedulers.from(serviceExecutor));
|
||||
|
||||
CallManager callManager = null;
|
||||
try {
|
||||
@@ -133,6 +142,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
||||
this)));
|
||||
}
|
||||
|
||||
public @NonNull Flowable<WebRtcEphemeralState> ephemeralStates() {
|
||||
return ephemeralStateStore.getStateFlowable();
|
||||
}
|
||||
|
||||
@NonNull CallManager getRingRtcCallManager() {
|
||||
//noinspection ConstantConditions
|
||||
return callManager;
|
||||
@@ -173,6 +186,16 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the given update to {@link WebRtcEphemeralState}.
|
||||
*
|
||||
* @param transformer The transformation to apply to the state. Runs on the {@link #serviceExecutor}.
|
||||
*/
|
||||
@AnyThread
|
||||
private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEphemeralState> transformer) {
|
||||
ephemeralStateStore.update(transformer);
|
||||
}
|
||||
|
||||
public void startPreJoinCall(@NonNull Recipient recipient) {
|
||||
process((s, p) -> p.handlePreJoinCall(s, new RemotePeer(recipient.getId())));
|
||||
}
|
||||
@@ -766,7 +789,7 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
|
||||
|
||||
@Override
|
||||
public void onAudioLevels(@NonNull GroupCall groupCall) {
|
||||
// TODO: Implement audio level handling for group calls.
|
||||
processStateless(s -> serviceState.getActionProcessor().handleGroupAudioLevelsChanged(serviceState, s));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.CallMetadata;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.OfferMetadata;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.ReceivedOfferMetadata;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder;
|
||||
import org.thoughtcrime.securesms.util.NetworkUtil;
|
||||
@@ -77,6 +78,8 @@ import static org.thoughtcrime.securesms.service.webrtc.WebRtcData.ReceivedAnswe
|
||||
*/
|
||||
public abstract class WebRtcActionProcessor {
|
||||
|
||||
public static final int AUDIO_LEVELS_INTERVAL = 200;
|
||||
|
||||
protected final Context context;
|
||||
protected final WebRtcInteractor webRtcInteractor;
|
||||
protected final String tag;
|
||||
@@ -680,6 +683,10 @@ public abstract class WebRtcActionProcessor {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
protected @NonNull WebRtcEphemeralState handleGroupAudioLevelsChanged(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState) {
|
||||
return ephemeralState;
|
||||
}
|
||||
|
||||
protected @NonNull WebRtcServiceState handleGroupJoinedMembershipChanged(@NonNull WebRtcServiceState currentState) {
|
||||
Log.i(tag, "handleGroupJoinedMembershipChanged not processed");
|
||||
return currentState;
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents the participants to be displayed in the grid at any given time.
|
||||
@@ -90,6 +92,10 @@ public class ParticipantCollection {
|
||||
return participants;
|
||||
}
|
||||
|
||||
public @NonNull ParticipantCollection map(@NonNull Function<CallParticipant, CallParticipant> mapper) {
|
||||
return new ParticipantCollection(maxGridCellCount, participants.stream().map(mapper).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return participants.size();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.thoughtcrime.securesms.service.webrtc.state
|
||||
|
||||
import org.thoughtcrime.securesms.events.CallParticipant
|
||||
import org.thoughtcrime.securesms.events.CallParticipantId
|
||||
|
||||
/**
|
||||
* The state of the call system which contains data which changes frequently.
|
||||
*/
|
||||
data class WebRtcEphemeralState(
|
||||
val localAudioLevel: CallParticipant.AudioLevel? = null,
|
||||
val remoteAudioLevels: Map<CallParticipantId, CallParticipant.AudioLevel> = emptyMap(),
|
||||
)
|
||||
Reference in New Issue
Block a user