WIP9
This commit is contained in:
parent
ecf7eab3ee
commit
6fe90e60db
4 changed files with 85 additions and 9 deletions
|
@ -53,10 +53,14 @@ export class DeviceMessageHandler {
|
||||||
}
|
}
|
||||||
const newRoomKeys = this._megolmDecryption.roomKeysFromDeviceMessages(olmDecryptChanges.results, log);
|
const newRoomKeys = this._megolmDecryption.roomKeysFromDeviceMessages(olmDecryptChanges.results, log);
|
||||||
const callMessages = olmDecryptChanges.results.filter(dr => this._callHandler.handlesDeviceMessageEventType(dr.event?.type));
|
const callMessages = olmDecryptChanges.results.filter(dr => this._callHandler.handlesDeviceMessageEventType(dr.event?.type));
|
||||||
|
// load devices by sender key
|
||||||
await Promise.all(callMessages.map(async dr => {
|
await Promise.all(callMessages.map(async dr => {
|
||||||
dr.setDevice(await this._getDevice(dr.senderCurve25519Key, txn));
|
dr.setDevice(await this._getDevice(dr.senderCurve25519Key, txn));
|
||||||
this._callHandler.handleDeviceMessage(dr.device.userId, dr.device.deviceId, dr.event.type, dr.event.content, log);
|
|
||||||
}));
|
}));
|
||||||
|
// TODO: pass this in the prep and run it in afterSync or afterSyncComplete (as callHandler can send events as well)?
|
||||||
|
for (const dr of callMessages) {
|
||||||
|
this._callHandler.handleDeviceMessage(dr.device.userId, dr.device.deviceId, dr.event.type, dr.event.content, log);
|
||||||
|
}
|
||||||
// TODO: somehow include rooms that received a call to_device message in the sync state?
|
// TODO: somehow include rooms that received a call to_device message in the sync state?
|
||||||
// or have updates flow through event emitter?
|
// or have updates flow through event emitter?
|
||||||
// well, we don't really need to update the room other then when a call starts or stops
|
// well, we don't really need to update the room other then when a call starts or stops
|
||||||
|
|
|
@ -109,8 +109,7 @@ class GroupParticipant implements PeerCallHandler {
|
||||||
|
|
||||||
sendInvite() {
|
sendInvite() {
|
||||||
this.peerCall = new PeerCall(this, this.webRTC);
|
this.peerCall = new PeerCall(this, this.webRTC);
|
||||||
this.peerCall.setLocalMedia(this.localMedia);
|
this.peerCall.call(this.localMedia);
|
||||||
this.peerCall.sendOffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** From PeerCallHandler
|
/** From PeerCallHandler
|
||||||
|
@ -140,7 +139,7 @@ class GroupCall {
|
||||||
async participate(tracks: Track[]) {
|
async participate(tracks: Track[]) {
|
||||||
this.localMedia = LocalMedia.fromTracks(tracks);
|
this.localMedia = LocalMedia.fromTracks(tracks);
|
||||||
for (const [,participant] of this.participants) {
|
for (const [,participant] of this.participants) {
|
||||||
participant.setLocalMedia(this.localMedia.clone());
|
participant.setMedia(this.localMedia.clone());
|
||||||
}
|
}
|
||||||
// send m.call.member state event
|
// send m.call.member state event
|
||||||
|
|
||||||
|
|
|
@ -86,13 +86,23 @@ class PeerCall {
|
||||||
handleIncomingSignallingMessage(message: SignallingMessage, partyId: PartyId) {
|
handleIncomingSignallingMessage(message: SignallingMessage, partyId: PartyId) {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case EventType.Invite:
|
case EventType.Invite:
|
||||||
this.handleInvite(message.content, partyId);
|
// determining whether or not an incoming invite glares
|
||||||
|
// with an instance of PeerCall is different for group calls
|
||||||
|
// and 1:1 calls, so done outside of this class.
|
||||||
|
// If you pass an event for another call id in here it will assume it glares.
|
||||||
|
|
||||||
|
//const newCallId = message.content.call_id;
|
||||||
|
//if (this.id && newCallId !== this.id) {
|
||||||
|
// this.handleInviteGlare(message.content);
|
||||||
|
//} else {
|
||||||
|
this.handleInvite(message.content, partyId);
|
||||||
|
//}
|
||||||
break;
|
break;
|
||||||
case EventType.Answer:
|
case EventType.Answer:
|
||||||
this.handleAnswer(message.content, partyId);
|
this.handleAnswer(message.content, partyId);
|
||||||
break;
|
break;
|
||||||
case EventType.Candidates:
|
case EventType.Candidates:
|
||||||
this.handleRemoteIceCandidates(message.content);
|
this.handleRemoteIceCandidates(message.content, partyId);
|
||||||
break;
|
break;
|
||||||
case EventType.Hangup:
|
case EventType.Hangup:
|
||||||
}
|
}
|
||||||
|
@ -184,6 +194,8 @@ class PeerCall {
|
||||||
|
|
||||||
// calls are serialized and deduplicated by responsePromiseChain
|
// calls are serialized and deduplicated by responsePromiseChain
|
||||||
private handleNegotiation = async (): Promise<void> => {
|
private handleNegotiation = async (): Promise<void> => {
|
||||||
|
// TODO: does this make sense to have this state if we're already connected?
|
||||||
|
this.setState(CallState.MakingOffer)
|
||||||
try {
|
try {
|
||||||
await this.peerConnection.setLocalDescription();
|
await this.peerConnection.setLocalDescription();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -221,7 +233,7 @@ class PeerCall {
|
||||||
}
|
}
|
||||||
this.sendCandidateQueue();
|
this.sendCandidateQueue();
|
||||||
|
|
||||||
if (this.state === CallState.CreateOffer) {
|
if (this.state === CallState.InviteSent) {
|
||||||
await this.delay(CALL_TIMEOUT_MS);
|
await this.delay(CALL_TIMEOUT_MS);
|
||||||
// @ts-ignore TS doesn't take the await above into account to know that the state could have changed in between
|
// @ts-ignore TS doesn't take the await above into account to know that the state could have changed in between
|
||||||
if (this.state === CallState.InviteSent) {
|
if (this.state === CallState.InviteSent) {
|
||||||
|
@ -495,7 +507,7 @@ class PeerCall {
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.disposables.dispose();
|
this.disposables.dispose();
|
||||||
// TODO: dispose peerConnection?
|
this.peerConnection.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,9 +541,9 @@ export enum CallParty {
|
||||||
|
|
||||||
export enum CallState {
|
export enum CallState {
|
||||||
Fledgling = 'fledgling',
|
Fledgling = 'fledgling',
|
||||||
InviteSent = 'invite_sent',
|
|
||||||
WaitLocalMedia = 'wait_local_media',
|
WaitLocalMedia = 'wait_local_media',
|
||||||
CreateOffer = 'create_offer',
|
CreateOffer = 'create_offer',
|
||||||
|
InviteSent = 'invite_sent',
|
||||||
CreateAnswer = 'create_answer',
|
CreateAnswer = 'create_answer',
|
||||||
Connecting = 'connecting',
|
Connecting = 'connecting',
|
||||||
Connected = 'connected',
|
Connected = 'connected',
|
||||||
|
|
|
@ -124,3 +124,64 @@ write view
|
||||||
- you send m.call.negotiate
|
- you send m.call.negotiate
|
||||||
- SOLVED: wrt to MSC2746, is the screen share track and the audio track (and video track) part of the same stream? or do screen share tracks need to go in a different stream? it sounds incompatible with the MSC2746 requirement.
|
- SOLVED: wrt to MSC2746, is the screen share track and the audio track (and video track) part of the same stream? or do screen share tracks need to go in a different stream? it sounds incompatible with the MSC2746 requirement.
|
||||||
- SOLVED: how does muting work? MediaStreamTrack.enabled
|
- SOLVED: how does muting work? MediaStreamTrack.enabled
|
||||||
|
- SOLVED: so, what's the difference between the call_id and the conf_id in group call events?
|
||||||
|
- call_id is the specific 1:1 call, conf_id is the thing in the m.call state event key
|
||||||
|
- so a group call has a conf_id with MxN peer calls, each having their call_id.
|
||||||
|
|
||||||
|
I think we need to synchronize the negotiation needed because we don't use a CallState to guard it...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Thursday 3-3 notes
|
||||||
|
|
||||||
|
we probably best keep the perfect negotiation flags, as they are needed for both starting the call AND renegotiation? if only for the former, it would make sense as it is a step in setting up the call, but if the call is ongoing, does it make sense to have a MakingOffer state? it actually looks like they are only needed for renegotiation! for call setup we compare the call_ids. What does that mean for these flags?
|
||||||
|
|
||||||
|
|
||||||
|
List state transitions
|
||||||
|
|
||||||
|
FROM CALLER FROM CALLEE
|
||||||
|
|
||||||
|
Fledgling Fledgling
|
||||||
|
V calling `call()` V handleInvite
|
||||||
|
WaitLocalMedia Ringing
|
||||||
|
V media promise resolves V answer()
|
||||||
|
CreateOffer WaitLocalMedia
|
||||||
|
V add tracks V media promise resolves
|
||||||
|
V wait for negotionneeded events CreateAnswer
|
||||||
|
V setLocalDescription() V
|
||||||
|
V send invite events
|
||||||
|
InviteSent
|
||||||
|
V receive anwser, setRemoteDescription() |
|
||||||
|
\__________________________________________________/
|
||||||
|
V
|
||||||
|
Connecting
|
||||||
|
V receive ice candidates and
|
||||||
|
iceConnectionState becomes 'connected'
|
||||||
|
Connected
|
||||||
|
V hangup for some reason
|
||||||
|
Ended
|
||||||
|
|
||||||
|
## From callee
|
||||||
|
|
||||||
|
Fledgling
|
||||||
|
Ringing
|
||||||
|
WaitLocalMedia
|
||||||
|
CreateAnswer
|
||||||
|
Connecting
|
||||||
|
Connected
|
||||||
|
Ended
|
||||||
|
|
||||||
|
Fledgling
|
||||||
|
WaitLocalMedia
|
||||||
|
CreateOffer
|
||||||
|
InviteSent
|
||||||
|
CreateAnswer
|
||||||
|
Connecting
|
||||||
|
Connected
|
||||||
|
Ringing
|
||||||
|
Ended
|
||||||
|
|
||||||
|
so if we don't want to bother with having two call objects, we can make the existing call hangup his old call_id? That way we keep the old peerConnection.
|
||||||
|
|
||||||
|
|
||||||
|
when glare, won't we drop both calls? No: https://github.com/matrix-org/matrix-spec-proposals/pull/2746#discussion_r819388754
|
||||||
|
|
Reference in a new issue