[FIX] discuss: rely on streaming state flags instead of streams
Before this commit, we used to rely on duplicates of streams and the presence of streams to know if a session was streaming. This commit changes this and make it so that we now use streaming activity flags instead. This is a prerequisite for https://github.com/odoo/odoo/pull/132153 as, similarly to webRTC "unified plan", the SFU keeps the same streams for the whole lifespan of a connection. closes odoo/odoo#140053 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -721,7 +721,7 @@ class Channel(models.Model):
|
||||
return []
|
||||
channel_infos = []
|
||||
# sudo: discuss.channel.rtc.session - reading sessions of accessible channel is acceptable
|
||||
rtc_sessions_by_channel = self.sudo().rtc_session_ids._mail_rtc_session_format_by_channel()
|
||||
rtc_sessions_by_channel = self.sudo().rtc_session_ids._mail_rtc_session_format_by_channel(extra=True)
|
||||
current_partner, current_guest = self.env["res.partner"]._get_current_persona()
|
||||
self.env['discuss.channel'].flush_model()
|
||||
self.env['discuss.channel.member'].flush_model()
|
||||
|
||||
@@ -60,9 +60,13 @@ class MailRtcSession(models.Model):
|
||||
of the change.
|
||||
"""
|
||||
valid_values = {'is_screen_sharing_on', 'is_camera_on', 'is_muted', 'is_deaf'}
|
||||
self.write({key: values[key] for key in valid_values if key in valid_values})
|
||||
session_data = self._mail_rtc_session_format()
|
||||
self.env['bus.bus']._sendone(self.channel_id, 'mail.record/insert', {'RtcSession': session_data})
|
||||
self.write({key: values[key] for key in valid_values if key in values})
|
||||
session_data = self._mail_rtc_session_format(extra=True)
|
||||
self.env["bus.bus"]._sendone(
|
||||
self.channel_id,
|
||||
"discuss.channel.rtc.session/update_and_broadcast",
|
||||
{"data": session_data, "channelId": self.channel_id.id},
|
||||
)
|
||||
|
||||
@api.autovacuum
|
||||
def _gc_inactive_sessions(self):
|
||||
@@ -95,29 +99,31 @@ class MailRtcSession(models.Model):
|
||||
payload_by_target[target]['notifications'].append(content)
|
||||
return self.env['bus.bus']._sendmany([(target, 'discuss.channel.rtc.session/peer_notification', payload) for target, payload in payload_by_target.items()])
|
||||
|
||||
def _mail_rtc_session_format(self, fields=None):
|
||||
def _mail_rtc_session_format(self, extra=False):
|
||||
self.ensure_one()
|
||||
if not fields:
|
||||
fields = {'id': True, 'channelMember': {'id': True, 'channel': {}, 'persona': {'partner': {'id', 'name', 'im_status'}, 'guest': {'id', 'name', 'im_status'}}}, 'isCameraOn': True, 'isDeaf': True, 'isSelfMuted': True, 'isScreenSharingOn': True}
|
||||
vals = {}
|
||||
if 'id' in fields:
|
||||
vals['id'] = self.id
|
||||
if 'channelMember' in fields:
|
||||
vals['channelMember'] = self.channel_member_id._discuss_channel_member_format(fields=fields.get('channelMember')).get(self.channel_member_id)
|
||||
if 'isCameraOn' in fields:
|
||||
vals['isCameraOn'] = self.is_camera_on
|
||||
if 'isDeaf' in fields:
|
||||
vals['isDeaf'] = self.is_deaf
|
||||
if 'isSelfMuted' in fields:
|
||||
vals['isSelfMuted'] = self.is_muted
|
||||
if 'isScreenSharingOn' in fields:
|
||||
vals['isScreenSharingOn'] = self.is_screen_sharing_on
|
||||
vals = {
|
||||
"id": self.id,
|
||||
"channelMember": self.channel_member_id._discuss_channel_member_format(
|
||||
fields={
|
||||
"id": True,
|
||||
"channel": {},
|
||||
"persona": {"partner": {"id", "name", "im_status"}, "guest": {"id", "name", "im_status"}},
|
||||
}
|
||||
).get(self.channel_member_id),
|
||||
}
|
||||
if extra:
|
||||
vals.update({
|
||||
"isCameraOn": self.is_camera_on,
|
||||
"isDeaf": self.is_deaf,
|
||||
"isSelfMuted": self.is_muted,
|
||||
"isScreenSharingOn": self.is_screen_sharing_on,
|
||||
})
|
||||
return vals
|
||||
|
||||
def _mail_rtc_session_format_by_channel(self):
|
||||
def _mail_rtc_session_format_by_channel(self, extra=False):
|
||||
data = {}
|
||||
for rtc_session in self:
|
||||
data.setdefault(rtc_session.channel_id, []).append(rtc_session._mail_rtc_session_format())
|
||||
data.setdefault(rtc_session.channel_id, []).append(rtc_session._mail_rtc_session_format(extra=extra))
|
||||
return data
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -487,9 +487,8 @@ export class Thread extends Record {
|
||||
}
|
||||
|
||||
get videoCount() {
|
||||
return Object.values(this._store.RtcSession.records).filter(
|
||||
(session) => session.videoStreams.size
|
||||
).length;
|
||||
return Object.values(this._store.RtcSession.records).filter((session) => session.hasVideo)
|
||||
.length;
|
||||
}
|
||||
|
||||
get lastInterestDateTime() {
|
||||
|
||||
@@ -90,19 +90,25 @@ export class Call extends Component {
|
||||
const filterVideos = this.props.thread.showOnlyVideo && this.props.thread.videoCount > 0;
|
||||
for (const session of this.props.thread.rtcSessions) {
|
||||
const target = session.raisingHand ? raisingHandCards : sessionCards;
|
||||
const cameraStream = session.videoStreams.get("camera");
|
||||
const cameraStream = session.isCameraOn
|
||||
? session.videoStreams.get("camera")
|
||||
: undefined;
|
||||
if (!filterVideos || cameraStream) {
|
||||
target.push({
|
||||
key: "session_main_" + session.id,
|
||||
session,
|
||||
type: "camera",
|
||||
videoStream: cameraStream,
|
||||
});
|
||||
}
|
||||
const screenStream = session.videoStreams.get("screen");
|
||||
const screenStream = session.isScreenSharingOn
|
||||
? session.videoStreams.get("screen")
|
||||
: undefined;
|
||||
if (screenStream) {
|
||||
target.push({
|
||||
key: "session_secondary_" + session.id,
|
||||
session,
|
||||
type: "screen",
|
||||
videoStream: screenStream,
|
||||
});
|
||||
}
|
||||
@@ -134,53 +140,34 @@ export class Call extends Component {
|
||||
/** @returns {CardData[]} */
|
||||
get visibleMainCards() {
|
||||
const activeSession = this.props.thread.activeRtcSession;
|
||||
this.state.insetCard = undefined;
|
||||
if (!activeSession) {
|
||||
this.state.insetCard = undefined;
|
||||
return this.visibleCards;
|
||||
}
|
||||
switch (activeSession.videoStreams.size) {
|
||||
case 1:
|
||||
if (activeSession.videoStreams.get("screen") === activeSession.mainVideoStream) {
|
||||
this.setInset(activeSession);
|
||||
}
|
||||
if (
|
||||
activeSession.videoStreams.get("camera") &&
|
||||
activeSession.videoStreams.get("camera") === activeSession.mainVideoStream
|
||||
) {
|
||||
this.state.insetCard = undefined;
|
||||
}
|
||||
if (!activeSession.mainVideoStream && activeSession.videoStreams.get("screen")) {
|
||||
this.setInset(activeSession, "screen");
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
const videoType =
|
||||
activeSession.videoStreams.get("screen") === activeSession.mainVideoStream
|
||||
? "camera"
|
||||
: "screen";
|
||||
this.setInset(activeSession, videoType);
|
||||
break;
|
||||
}
|
||||
const type = activeSession.mainVideoStreamType;
|
||||
if (type === "screen" || activeSession.isScreenSharingOn) {
|
||||
this.setInset(activeSession, type === "camera" ? "screen" : "camera");
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
key: "session_" + activeSession.id,
|
||||
session: activeSession,
|
||||
videoStream: activeSession.mainVideoStream,
|
||||
type,
|
||||
videoStream: activeSession.getStream(type),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {RtcSession} session
|
||||
* @param {String} videoType
|
||||
* @param {String} [videoType]
|
||||
*/
|
||||
setInset(session, videoType) {
|
||||
this.state.insetCard = {
|
||||
key: "session_" + session.id,
|
||||
session,
|
||||
videoStream: session.videoStreams.get(videoType),
|
||||
type: videoType,
|
||||
videoStream: session.getStream(videoType),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -119,20 +119,16 @@ export class CallParticipantCard extends Component {
|
||||
}
|
||||
if (this.rtcSession) {
|
||||
const channel = this.rtcSession.channel;
|
||||
this.rtcSession.mainVideoStreamType = this.props.cardData.type;
|
||||
if (this.rtcSession.eq(channel.activeRtcSession) && !this.props.inset) {
|
||||
channel.activeRtcSession = undefined;
|
||||
this.rtcSession.mainVideoStream = undefined;
|
||||
this.rtcSession.mainVideoStreamType = undefined;
|
||||
} else {
|
||||
const activeRtcSession = channel.activeRtcSession;
|
||||
const mainVideoStream = this.rtcSession.mainVideoStream;
|
||||
const currentMainVideoType = this.rtcSession.mainVideoStreamType;
|
||||
channel.activeRtcSession = this.rtcSession;
|
||||
this.rtcSession.mainVideoStream = this.props.cardData.videoStream;
|
||||
if (this.props.inset && activeRtcSession) {
|
||||
const videoType =
|
||||
activeRtcSession.videoStreams.get("camera") === mainVideoStream
|
||||
? "camera"
|
||||
: "screen";
|
||||
this.props.inset(activeRtcSession, videoType);
|
||||
this.props.inset(activeRtcSession, currentMainVideoType);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -273,6 +273,7 @@ export class Rtc {
|
||||
*/
|
||||
endCall(channel = this.state.channel) {
|
||||
channel.rtcInvitingSession = undefined;
|
||||
channel.activeRtcSession = undefined;
|
||||
if (channel.eq(this.state.channel)) {
|
||||
this.clear();
|
||||
this.soundEffectsService.play("channel-leave");
|
||||
@@ -442,6 +443,7 @@ export class Rtc {
|
||||
*/
|
||||
this.updateActiveSession(session, payload.type);
|
||||
session.videoStreams.delete(payload.type);
|
||||
session.updateStreamState(payload.type, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -634,12 +636,11 @@ export class Rtc {
|
||||
};
|
||||
peerConnection.ontrack = ({ transceiver, track }) => {
|
||||
this.log(session, `received ${track.kind} track`);
|
||||
const videoType = this.getVideoType(session.peerConnection, transceiver);
|
||||
this.updateStream(session, track, {
|
||||
mute: this.state.selfSession.isDeaf,
|
||||
videoType,
|
||||
this.handleRemoteTrack({
|
||||
session,
|
||||
track,
|
||||
type: this.getTransceiverType(session.peerConnection, transceiver),
|
||||
});
|
||||
this.updateActiveSession(session, videoType, { addVideo: true });
|
||||
};
|
||||
const dataChannel = peerConnection.createDataChannel("notifications", {
|
||||
negotiated: true,
|
||||
@@ -685,11 +686,26 @@ export class Rtc {
|
||||
return peerConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("@mail/discuss/call/common/rtc_session_model").RtcSession} session
|
||||
* @param {MediaStreamTrack} track
|
||||
* @param {"audio" | "screen" | "camera"} type
|
||||
* @param {boolean} active false if the track is muted/disabled
|
||||
*/
|
||||
async handleRemoteTrack({ session, track, type, active = true }) {
|
||||
session.updateStreamState(type, active);
|
||||
await this.updateStream(session, track, {
|
||||
mute: this.state.selfSession.isDeaf,
|
||||
videoType: type,
|
||||
});
|
||||
this.updateActiveSession(session, type, { addVideo: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {RTCPeerConnection} peerConnection
|
||||
* @param {RTCRtpTransceiver} transceiver
|
||||
*/
|
||||
getVideoType(peerConnection, transceiver) {
|
||||
getTransceiverType(peerConnection, transceiver) {
|
||||
const transceivers = peerConnection.getTransceivers();
|
||||
return ORDERED_TRANSCEIVER_NAMES[transceivers.indexOf(transceiver)];
|
||||
}
|
||||
@@ -904,6 +920,7 @@ export class Rtc {
|
||||
session.audioError = undefined;
|
||||
session.videoError = undefined;
|
||||
session.isTalking = false;
|
||||
session.mainVideoStreamType = undefined;
|
||||
this.removeVideoFromSession(session);
|
||||
session.dataChannel?.close();
|
||||
delete session.dataChannel;
|
||||
@@ -1291,7 +1308,7 @@ export class Rtc {
|
||||
}
|
||||
if (!track && trackKind !== "audio") {
|
||||
this.notify([session], "trackChange", {
|
||||
type: this.getVideoType(session.peerConnection, transceiver),
|
||||
type: this.getTransceiverType(session.peerConnection, transceiver),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1457,38 +1474,34 @@ export class Rtc {
|
||||
/**
|
||||
* @param {RtcSession} session
|
||||
* @param {"screen"|"camera"} [videoType]
|
||||
* @param {Object} [parm2]
|
||||
* @param {boolean} [parm2.addVideo]
|
||||
*/
|
||||
updateActiveSession(session, videoType, { addVideo = false } = {}) {
|
||||
const activeRtcSession = this.state.channel.activeRtcSession;
|
||||
if (addVideo) {
|
||||
if (videoType === "screen") {
|
||||
this.state.channel.activeRtcSession = session;
|
||||
session.mainVideoStream = session.videoStreams.get("screen");
|
||||
session.mainVideoStreamType = videoType;
|
||||
return;
|
||||
}
|
||||
if (activeRtcSession && session.videoStreams.size && !session.mainVideoStream) {
|
||||
session.mainVideoStream = session.videoStreams.get("camera");
|
||||
if (activeRtcSession && session.hasVideo && !session.isMainVideoStreamActive) {
|
||||
session.mainVideoStreamType = videoType;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!activeRtcSession || activeRtcSession.notEq(session)) {
|
||||
return;
|
||||
}
|
||||
if (activeRtcSession.mainVideoStream) {
|
||||
const activeVideoType =
|
||||
activeRtcSession.videoStreams.get("screen") === session.mainVideoStream
|
||||
? "screen"
|
||||
: "camera";
|
||||
if (videoType === activeVideoType) {
|
||||
session.mainVideoStream = undefined;
|
||||
if (activeRtcSession.isMainVideoStreamActive) {
|
||||
if (videoType === session.mainVideoStreamType) {
|
||||
if (videoType === "screen") {
|
||||
this.state.channel.activeRtcSession = undefined;
|
||||
} else {
|
||||
session.mainVideoStreamType = "screen";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!activeRtcSession.mainVideoStream && videoType === "screen") {
|
||||
session.mainVideoStream = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
updateVideoDownload(rtcSession, { viewCountIncrement }) {
|
||||
@@ -1581,6 +1594,19 @@ export const rtcService = {
|
||||
services["mail.user_settings"].setVolumes(payload);
|
||||
}
|
||||
});
|
||||
services["bus_service"].subscribe(
|
||||
"discuss.channel.rtc.session/update_and_broadcast",
|
||||
(payload) => {
|
||||
const { data, channelId } = payload;
|
||||
/**
|
||||
* If this event comes from the channel of the current call, information is shared in real time
|
||||
* through the peer to peer connection. So we do not use this less accurate broadcast.
|
||||
*/
|
||||
if (channelId !== rtc.state.channel?.id) {
|
||||
rtc.store.RtcSession.insert(data);
|
||||
}
|
||||
}
|
||||
);
|
||||
return rtc;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -22,12 +22,18 @@ export class RtcSession extends Record {
|
||||
}
|
||||
|
||||
// Server data
|
||||
/** @type {boolean} */
|
||||
channelMember = Record.one("ChannelMember", { inverse: "rtcSession" });
|
||||
/** @type {boolean} */
|
||||
isCameraOn;
|
||||
id;
|
||||
isDeaf;
|
||||
isSelfMuted;
|
||||
/** @type {boolean} */
|
||||
isScreenSharingOn;
|
||||
/** @type {number} */
|
||||
id;
|
||||
/** @type {boolean} */
|
||||
isDeaf;
|
||||
/** @type {boolean} */
|
||||
isSelfMuted;
|
||||
// Client data
|
||||
/** @type {HTMLAudioElement} */
|
||||
audioElement;
|
||||
@@ -46,8 +52,8 @@ export class RtcSession extends Record {
|
||||
videoComponentCount = 0;
|
||||
/** @type {Map<'screen'|'camera', MediaStream>} */
|
||||
videoStreams = new Map();
|
||||
/** @type {MediaStream} */
|
||||
mainVideoStream;
|
||||
/** @type {string} */
|
||||
mainVideoStreamType;
|
||||
// RTC stats
|
||||
connectionState;
|
||||
localCandidateType;
|
||||
@@ -68,6 +74,26 @@ export class RtcSession extends Record {
|
||||
return this.isSelfMuted || this.isDeaf;
|
||||
}
|
||||
|
||||
get mainVideoStream() {
|
||||
return this.isMainVideoStreamActive && this.videoStreams.get(this.mainVideoStreamType);
|
||||
}
|
||||
|
||||
get isMainVideoStreamActive() {
|
||||
if (!this.mainVideoStreamType) {
|
||||
return false;
|
||||
}
|
||||
return this.mainVideoStreamType === "camera" ? this.isCameraOn : this.isScreenSharingOn;
|
||||
}
|
||||
|
||||
get hasVideo() {
|
||||
return this.isScreenSharingOn || this.isCameraOn;
|
||||
}
|
||||
|
||||
getStream(type) {
|
||||
const isActive = type === "camera" ? this.isCameraOn : this.isScreenSharingOn;
|
||||
return isActive && this.videoStreams.get(type);
|
||||
}
|
||||
|
||||
get partnerId() {
|
||||
const persona = this.channelMember?.persona;
|
||||
return persona.type === "partner" ? persona.id : undefined;
|
||||
@@ -111,6 +137,18 @@ export class RtcSession extends Record {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {"audio" | "camera" | "screen"} type
|
||||
* @param {boolean} state
|
||||
*/
|
||||
updateStreamState(type, state) {
|
||||
if (type === "camera") {
|
||||
this.isCameraOn = state;
|
||||
} else if (type === "screen") {
|
||||
this.isScreenSharingOn = state;
|
||||
}
|
||||
}
|
||||
|
||||
async updateStats() {
|
||||
delete this.localCandidateType;
|
||||
delete this.remoteCandidateType;
|
||||
|
||||
@@ -718,7 +718,8 @@ patch(MockServer.prototype, {
|
||||
"ADD",
|
||||
(channel.rtc_session_ids || []).map((rtcSessionId) =>
|
||||
this._mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(
|
||||
rtcSessionId
|
||||
rtcSessionId,
|
||||
{ extra: true }
|
||||
)
|
||||
),
|
||||
],
|
||||
|
||||
@@ -11,18 +11,23 @@ patch(MockServer.prototype, {
|
||||
* @param {integer} id
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(id) {
|
||||
_mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(id, { extra = false } = {}) {
|
||||
const [rtcSession] = this.getRecords("discuss.channel.rtc.session", [["id", "=", id]]);
|
||||
return {
|
||||
const vals = {
|
||||
id: rtcSession.id,
|
||||
channelMember: this._mockDiscussChannelMember_DiscussChannelMemberFormat([
|
||||
rtcSession.channel_member_id,
|
||||
])[0],
|
||||
isCameraOn: rtcSession.is_camera_on,
|
||||
isDeaf: rtcSession.is_deaf,
|
||||
isSelfMuted: rtcSession.is_self_muted,
|
||||
isScreenSharingOn: rtcSession.is_screen_sharing_on,
|
||||
};
|
||||
if (extra) {
|
||||
Object.assign(vals, {
|
||||
isCameraOn: rtcSession.is_camera_on,
|
||||
isDeaf: rtcSession.is_deaf,
|
||||
isSelfMuted: rtcSession.is_self_muted,
|
||||
isScreenSharingOn: rtcSession.is_screen_sharing_on,
|
||||
});
|
||||
}
|
||||
return vals;
|
||||
},
|
||||
/**
|
||||
* Simulates `_mail_rtc_session_format` on `discuss.channel.rtc.session`.
|
||||
@@ -31,7 +36,10 @@ patch(MockServer.prototype, {
|
||||
* @param {integer[]} ids
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormatByChannel(ids) {
|
||||
_mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormatByChannel(
|
||||
ids,
|
||||
{ extra = false } = {}
|
||||
) {
|
||||
const rtcSessions = this.getRecords("discuss.channel.rtc.session", [["id", "in", ids]]);
|
||||
const data = {};
|
||||
for (const rtcSession of rtcSessions) {
|
||||
@@ -39,7 +47,9 @@ patch(MockServer.prototype, {
|
||||
data[rtcSession.channel_id] = [];
|
||||
}
|
||||
data[rtcSession.channel_id].push(
|
||||
this._mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(rtcSession.id)
|
||||
this._mockDiscussChannelRtcSession_DiscussChannelRtcSessionFormat(rtcSession.id, {
|
||||
extra,
|
||||
})
|
||||
);
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -55,10 +55,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
}])],
|
||||
},
|
||||
},
|
||||
@@ -80,10 +76,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
}]),
|
||||
('DELETE', [{'id': channel_member.rtc_session_ids.id - 1}]),
|
||||
],
|
||||
@@ -127,10 +119,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
}])],
|
||||
},
|
||||
},
|
||||
@@ -209,10 +197,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
}])],
|
||||
},
|
||||
},
|
||||
@@ -232,10 +216,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
}])],
|
||||
},
|
||||
},
|
||||
@@ -362,10 +342,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
},
|
||||
])],
|
||||
},
|
||||
@@ -429,10 +405,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "guest",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
},
|
||||
])],
|
||||
},
|
||||
@@ -670,10 +642,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -696,10 +664,6 @@ class TestChannelRTC(MailCommon):
|
||||
"type": "partner",
|
||||
},
|
||||
},
|
||||
'isCameraOn': False,
|
||||
'isDeaf': False,
|
||||
'isSelfMuted': False,
|
||||
'isScreenSharingOn': False,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user