mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
changes
This commit is contained in:
+16
-13
@@ -119,7 +119,7 @@ actual fun ActiveCallView() {
|
||||
val callRh = call.remoteHostId
|
||||
when (val r = apiMsg.resp) {
|
||||
is WCallResponse.Capabilities -> withBGApi {
|
||||
val callType = CallType(call.localMedia, r.capabilities)
|
||||
val callType = CallType(call.initialCallType, r.capabilities)
|
||||
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) }
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
@@ -132,7 +132,7 @@ actual fun ActiveCallView() {
|
||||
activeCallWaitDeliveryReceipt(scope)
|
||||
}
|
||||
is WCallResponse.Offer -> withBGApi {
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities)
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.initialCallType, r.capabilities)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.OfferSent, localCapabilities = r.capabilities) }
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
// Starting is delayed to make Android <= 11 working good with Bluetooth
|
||||
@@ -185,16 +185,19 @@ actual fun ActiveCallView() {
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Negotiated) }
|
||||
is WCallCommand.Media -> {
|
||||
updateActiveCall(call) {
|
||||
when (cmd.media) {
|
||||
CallMediaType.Video -> it.copy(videoEnabled = cmd.enable, localMedia = if (cmd.enable) CallMediaType.Video else CallMediaType.Audio)
|
||||
CallMediaType.Audio -> it.copy(audioEnabled = cmd.enable)
|
||||
val sources = it.localMediaSources
|
||||
when (cmd.source) {
|
||||
CallMediaSource.Mic -> it.copy(localMediaSources = sources.copy(mic = cmd.enable))
|
||||
CallMediaSource.Camera -> it.copy(localMediaSources = sources.copy(camera = cmd.enable))
|
||||
CallMediaSource.ScreenAudio -> it.copy(localMediaSources = sources.copy(screenAudio = cmd.enable))
|
||||
CallMediaSource.ScreenVideo -> it.copy(localMediaSources = sources.copy(screenVideo = cmd.enable))
|
||||
}
|
||||
}
|
||||
}
|
||||
is WCallCommand.Camera -> {
|
||||
updateActiveCall(call) { it.copy(localCamera = cmd.camera) }
|
||||
if (!call.audioEnabled) {
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = false))
|
||||
if (!call.localMediaSources.mic) {
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = false))
|
||||
}
|
||||
}
|
||||
is WCallCommand.End -> {
|
||||
@@ -248,9 +251,9 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceM
|
||||
devices = remember { callAudioDeviceManager.devices }.value,
|
||||
currentDevice = remember { callAudioDeviceManager.currentDevice },
|
||||
dismiss = { withBGApi { chatModel.callManager.endCall(call) } },
|
||||
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) },
|
||||
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = !call.localMediaSources.mic)) },
|
||||
selectDevice = { callAudioDeviceManager.selectDevice(it.id) },
|
||||
toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) },
|
||||
toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Camera, enable = !call.localMediaSources.camera)) },
|
||||
toggleSound = {
|
||||
val enableSpeaker = callAudioDeviceManager.currentDevice.value?.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
val preferredInternalDevice = callAudioDeviceManager.devices.value.firstOrNull { it.type == if (enableSpeaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
|
||||
@@ -365,7 +368,7 @@ private fun ActiveCallOverlayLayout(
|
||||
IconButton(onClick = dismiss, enabled = enabled) {
|
||||
Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp))
|
||||
}
|
||||
if (call.videoEnabled) {
|
||||
if (call.localMediaSources.camera) {
|
||||
ControlButton(call, painterResource(MR.images.ic_flip_camera_android_filled), MR.strings.icon_descr_flip_camera, enabled, flipCamera)
|
||||
ControlButton(call, painterResource(MR.images.ic_videocam_filled), MR.strings.icon_descr_video_off, enabled, toggleVideo)
|
||||
} else {
|
||||
@@ -390,7 +393,7 @@ private fun ControlButton(call: Call, icon: Painter, iconText: StringResource, e
|
||||
|
||||
@Composable
|
||||
private fun ToggleAudioButton(call: Call, enabled: Boolean = true, toggleAudio: () -> Unit) {
|
||||
if (call.audioEnabled) {
|
||||
if (call.localMediaSources.mic) {
|
||||
ControlButton(call, painterResource(MR.images.ic_mic), MR.strings.icon_descr_audio_off, enabled, toggleAudio)
|
||||
} else {
|
||||
ControlButton(call, painterResource(MR.images.ic_mic_off), MR.strings.icon_descr_audio_on, enabled, toggleAudio)
|
||||
@@ -762,7 +765,7 @@ fun PreviewActiveCallOverlayVideo() {
|
||||
userProfile = Profile.sampleData,
|
||||
contact = Contact.sampleData,
|
||||
callState = CallState.Negotiated,
|
||||
localMedia = CallMediaType.Video,
|
||||
initialCallType = CallMediaType.Video,
|
||||
peerMediaSources = CallMediaSources(),
|
||||
callUUID = "",
|
||||
connectionInfo = ConnectionInfo(
|
||||
@@ -792,7 +795,7 @@ fun PreviewActiveCallOverlayAudio() {
|
||||
userProfile = Profile.sampleData,
|
||||
contact = Contact.sampleData,
|
||||
callState = CallState.Negotiated,
|
||||
localMedia = CallMediaType.Audio,
|
||||
initialCallType = CallMediaType.Audio,
|
||||
peerMediaSources = CallMediaSources(),
|
||||
callUUID = "",
|
||||
connectionInfo = ConnectionInfo(
|
||||
|
||||
+1
-2
@@ -54,8 +54,7 @@ actual fun ActiveCallInteractiveArea(call: Call) {
|
||||
.align(Alignment.BottomCenter),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val media = call.peerMediaSources ?: call.localMedia
|
||||
if (media == CallMediaType.Video) {
|
||||
if (call.supportsVideo()) {
|
||||
Icon(painterResource(MR.images.ic_videocam_filled), null, Modifier.size(27.dp).offset(x = 2.5.dp, y = 2.dp), tint = Color.White)
|
||||
} else {
|
||||
Icon(painterResource(MR.images.ic_call_filled), null, Modifier.size(27.dp).offset(x = -0.5.dp, y = 2.dp), tint = Color.White)
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class CallManager(val chatModel: ChatModel) {
|
||||
contact = invitation.contact,
|
||||
callUUID = invitation.callUUID,
|
||||
callState = CallState.InvitationAccepted,
|
||||
localMedia = invitation.callType.media,
|
||||
initialCallType = invitation.callType.media,
|
||||
sharedKey = invitation.sharedKey,
|
||||
)
|
||||
showCallView.value = true
|
||||
|
||||
+7
-4
@@ -2,6 +2,7 @@ package chat.simplex.common.views.call
|
||||
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.SerialName
|
||||
@@ -15,12 +16,11 @@ data class Call(
|
||||
val contact: Contact,
|
||||
val callUUID: String?,
|
||||
val callState: CallState,
|
||||
val localMedia: CallMediaType,
|
||||
val initialCallType: CallMediaType,
|
||||
val localMediaSources: CallMediaSources = CallMediaSources(mic = true, camera = initialCallType == CallMediaType.Video && appPlatform.isAndroid),
|
||||
val localCapabilities: CallCapabilities? = null,
|
||||
val peerMediaSources: CallMediaSources = CallMediaSources(),
|
||||
val sharedKey: String? = null,
|
||||
val audioEnabled: Boolean = true,
|
||||
val videoEnabled: Boolean = localMedia == CallMediaType.Video,
|
||||
var localCamera: VideoCamera = VideoCamera.User,
|
||||
val connectionInfo: ConnectionInfo? = null,
|
||||
var connectedAt: Instant? = null,
|
||||
@@ -35,6 +35,9 @@ data class Call(
|
||||
else -> generalGetString(if (!localEncrypted) MR.strings.status_no_e2e_encryption else if (sharedKey == null) MR.strings.status_contact_has_no_e2e_encryption else MR.strings.status_e2e_encrypted)
|
||||
}
|
||||
|
||||
val localMedia: CallMediaType
|
||||
get() = if (localMediaSources.camera) CallMediaType.Video else CallMediaType.Audio
|
||||
|
||||
val hasMedia: Boolean get() = callState == CallState.OfferSent || callState == CallState.Negotiated || callState == CallState.Connected
|
||||
|
||||
fun supportsVideo(): Boolean = peerMediaSources.hasVideo() || localMedia == CallMediaType.Video
|
||||
@@ -84,7 +87,7 @@ sealed class WCallCommand {
|
||||
@Serializable @SerialName("offer") data class Offer(val offer: String, val iceCandidates: String, val media: CallMediaType, val aesKey: String? = null, val iceServers: List<RTCIceServer>? = null, val relay: Boolean? = null): WCallCommand()
|
||||
@Serializable @SerialName("answer") data class Answer (val answer: String, val iceCandidates: String): WCallCommand()
|
||||
@Serializable @SerialName("ice") data class Ice(val iceCandidates: String): WCallCommand()
|
||||
@Serializable @SerialName("media") data class Media(val media: CallMediaType, val enable: Boolean): WCallCommand()
|
||||
@Serializable @SerialName("media") data class Media(val source: CallMediaSource, val enable: Boolean): WCallCommand()
|
||||
@Serializable @SerialName("camera") data class Camera(val camera: VideoCamera): WCallCommand()
|
||||
@Serializable @SerialName("description") data class Description(val state: String, val description: String): WCallCommand()
|
||||
@Serializable @SerialName("layout") data class Layout(val layout: LayoutType): WCallCommand()
|
||||
|
||||
+1
-1
@@ -545,7 +545,7 @@ fun startChatCall(remoteHostId: Long?, chatInfo: ChatInfo, media: CallMediaType)
|
||||
if (chatInfo is ChatInfo.Direct) {
|
||||
val contactInfo = chatModel.controller.apiContactInfo(remoteHostId, chatInfo.contact.contactId)
|
||||
val profile = contactInfo?.second ?: chatModel.currentUser.value?.profile?.toProfile() ?: return@withBGApi
|
||||
chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callUUID = null, callState = CallState.WaitCapabilities, localMedia = media, userProfile = profile)
|
||||
chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callUUID = null, callState = CallState.WaitCapabilities, initialCallType = media, userProfile = profile)
|
||||
chatModel.showCallView.value = true
|
||||
chatModel.callCommand.add(WCallCommand.Capabilities(media))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
<script src="../lz-string.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="visibility: hidden"
|
||||
onclick="javascript:toggleRemoteScreenVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
@@ -15,15 +24,6 @@
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="local-video-stream"
|
||||
class="inline"
|
||||
@@ -39,7 +39,7 @@
|
||||
muted
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
></video>
|
||||
</body>
|
||||
<footer>
|
||||
|
||||
@@ -12,6 +12,17 @@ body {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#remote-video-stream.collapsed {
|
||||
position: absolute;
|
||||
max-width: 30%;
|
||||
max-height: 30%;
|
||||
object-fit: cover;
|
||||
margin: 16px;
|
||||
border-radius: 16px;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#remote-screen-video-stream.inline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -136,7 +136,16 @@ const processCommand = (function () {
|
||||
const remoteStream = new MediaStream();
|
||||
const remoteScreenStream = new MediaStream();
|
||||
const localCamera = VideoCamera.User;
|
||||
const localStream = await getLocalMediaStream(mediaType, localCamera);
|
||||
let localStream;
|
||||
try {
|
||||
localStream = await getLocalMediaStream(mediaType, localCamera);
|
||||
}
|
||||
catch (e) {
|
||||
if (isDesktop) {
|
||||
window.alert("Permission denied. Please, allow mic and video to make the call working.");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
const localScreenStream = new MediaStream();
|
||||
if (isDesktop) {
|
||||
localStream
|
||||
@@ -355,12 +364,12 @@ const processCommand = (function () {
|
||||
if (!activeCall) {
|
||||
resp = { type: "error", message: "media: call not started" };
|
||||
}
|
||||
else if (!activeCall.cameraTrackWasSetBefore && command.media == CallMediaType.Video && command.enable) {
|
||||
else if (!activeCall.cameraTrackWasSetBefore && command.source == CallMediaSource.Camera && command.enable) {
|
||||
await startSendingCamera(activeCall, activeCall.localCamera);
|
||||
resp = { type: "ok" };
|
||||
}
|
||||
else {
|
||||
enableMedia(activeCall.localStream, command.media, command.enable);
|
||||
enableMedia(activeCall.localStream, command.source, command.enable);
|
||||
resp = { type: "ok" };
|
||||
}
|
||||
break;
|
||||
@@ -629,7 +638,7 @@ const processCommand = (function () {
|
||||
// videos.localScreen.pause()
|
||||
// videos.localScreen.srcObject = call.localScreenStream
|
||||
videos.localScreen.play();
|
||||
videos.localScreen.style.display = "block";
|
||||
videos.localScreen.style.visibility = "visible";
|
||||
}
|
||||
else {
|
||||
pc.getTransceivers().forEach((elem) => {
|
||||
@@ -642,7 +651,7 @@ const processCommand = (function () {
|
||||
t.stop();
|
||||
for (const t of call.localScreenStream.getTracks())
|
||||
call.localScreenStream.removeTrack(t);
|
||||
videos.localScreen.style.display = "none";
|
||||
videos.localScreen.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
async function replaceMedia(call, camera) {
|
||||
@@ -727,56 +736,62 @@ const processCommand = (function () {
|
||||
const videos = getVideoElements();
|
||||
if (!videos)
|
||||
throw Error("no video elements");
|
||||
if (activeCall) {
|
||||
const source = mediaSourceFromTransceiverMid(transceiverMid);
|
||||
console.log("LALAL ON MUTE/UNMUTE", mute, source, transceiverMid);
|
||||
const sources = activeCall.peerMediaSources;
|
||||
if (source == CallMediaSource.Mic && activeCall.peerMediaSources.mic == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.mic = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.Camera && activeCall.peerMediaSources.camera == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.camera = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
videos.remote.style.display = !mute ? "block" : "none";
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.ScreenAudio && activeCall.peerMediaSources.screenAudio == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.screenAudio = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.ScreenVideo && activeCall.peerMediaSources.screenVideo == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.screenVideo = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
videos.remoteScreen.style.display = !mute ? "block" : "none";
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
if (!activeCall)
|
||||
return;
|
||||
const source = mediaSourceFromTransceiverMid(transceiverMid);
|
||||
console.log("LALAL ON MUTE/UNMUTE", mute, source, transceiverMid);
|
||||
const sources = activeCall.peerMediaSources;
|
||||
if (source == CallMediaSource.Mic && activeCall.peerMediaSources.mic == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.mic = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.Camera && activeCall.peerMediaSources.camera == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.camera = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
videos.remote.style.visibility = !mute ? "visible" : "hidden";
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.ScreenAudio && activeCall.peerMediaSources.screenAudio == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.screenAudio = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
else if (source == CallMediaSource.ScreenVideo && activeCall.peerMediaSources.screenVideo == mute) {
|
||||
const resp = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
};
|
||||
sources.screenVideo = !mute;
|
||||
activeCall.peerMediaSources = sources;
|
||||
videos.remoteScreen.style.visibility = !mute ? "visible" : "hidden";
|
||||
sendMessageToNative({ resp: resp });
|
||||
}
|
||||
if (activeCall.peerMediaSources.screenVideo) {
|
||||
videos.remote.className = "collapsed";
|
||||
}
|
||||
else {
|
||||
videos.remote.className = "inline";
|
||||
}
|
||||
}
|
||||
function getLocalMediaStream(mediaType, facingMode) {
|
||||
@@ -861,20 +876,23 @@ const processCommand = (function () {
|
||||
// video.style.opacity = "1"
|
||||
// }
|
||||
// }
|
||||
function enableMedia(s, media, enable) {
|
||||
const tracks = media == CallMediaType.Video ? s.getVideoTracks() : s.getAudioTracks();
|
||||
function enableMedia(s, source, enable) {
|
||||
const tracks = source == CallMediaSource.Camera ? s.getVideoTracks() : s.getAudioTracks();
|
||||
for (const t of tracks)
|
||||
activeCall === null || activeCall === void 0 ? void 0 : activeCall.connection.getTransceivers().forEach((elem) => {
|
||||
if (enable) {
|
||||
t.enabled = true;
|
||||
elem.sender.replaceTrack(t);
|
||||
}
|
||||
else {
|
||||
t.enabled = false;
|
||||
elem.sender.replaceTrack(null);
|
||||
if ((t.kind == CallMediaType.Audio && mediaSourceFromTransceiverMid(elem.mid) == CallMediaSource.Mic) ||
|
||||
(t.kind == CallMediaType.Video && mediaSourceFromTransceiverMid(elem.mid) == CallMediaSource.Camera)) {
|
||||
if (enable) {
|
||||
t.enabled = true;
|
||||
elem.sender.replaceTrack(t);
|
||||
}
|
||||
else {
|
||||
t.enabled = false;
|
||||
elem.sender.replaceTrack(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (media == CallMediaType.Video && activeCall) {
|
||||
if (source == CallMediaSource.Camera && activeCall) {
|
||||
activeCall.localMediaSources.camera = enable;
|
||||
}
|
||||
}
|
||||
@@ -892,6 +910,10 @@ function toggleRemoteVideoFitFill() {
|
||||
const remote = document.getElementById("remote-video-stream");
|
||||
remote.style.objectFit = remote.style.objectFit != "contain" ? "contain" : "cover";
|
||||
}
|
||||
function toggleRemoteScreenVideoFitFill() {
|
||||
const remoteScreen = document.getElementById("remote-screen-video-stream");
|
||||
remoteScreen.style.objectFit = remoteScreen.style.objectFit != "contain" ? "contain" : "cover";
|
||||
}
|
||||
function toggleMedia(s, media) {
|
||||
let res = false;
|
||||
const tracks = media == CallMediaType.Video ? s.getVideoTracks() : s.getAudioTracks();
|
||||
|
||||
@@ -7,20 +7,20 @@
|
||||
<script src="/lz-string.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
onclick="javascript:toggleRemoteScreenVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
muted
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
></video>
|
||||
|
||||
<div id="progress"></div>
|
||||
@@ -50,19 +50,19 @@
|
||||
<img src="/desktop/images/ic_phone_in_talk.svg" />
|
||||
</div>
|
||||
<p id="manage-call">
|
||||
<button id="toggle-screen" style="display: none" onclick="javascript:toggleScreenManually()">
|
||||
<button id="toggle-screen" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleScreenManually()">
|
||||
<img src="/desktop/images/ic_screen_share.svg" />
|
||||
</button>
|
||||
<button id="toggle-audio" style="display: none" onclick="javascript:toggleAudioManually()">
|
||||
<button id="toggle-audio" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleAudioManually()">
|
||||
<img src="/desktop/images/ic_mic.svg" />
|
||||
</button>
|
||||
<button id="end-call" onclick="javascript:endCallManually()">
|
||||
<img src="/desktop/images/ic_call_end_filled.svg" />
|
||||
</button>
|
||||
<button id="toggle-speaker" style="display: none" onclick="javascript:toggleSpeakerManually()">
|
||||
<button id="toggle-speaker" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleSpeakerManually()">
|
||||
<img src="/desktop/images/ic_volume_up.svg" />
|
||||
</button>
|
||||
<button id="toggle-video" style="display: none" onclick="javascript:toggleVideoManually()">
|
||||
<button id="toggle-video" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleVideoManually()">
|
||||
<img src="/desktop/images/ic_videocam_off.svg" />
|
||||
</button>
|
||||
</p>
|
||||
|
||||
@@ -12,6 +12,17 @@ body {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#remote-video-stream.collapsed {
|
||||
position: absolute;
|
||||
max-width: 20%;
|
||||
max-height: 20%;
|
||||
object-fit: cover;
|
||||
margin: 16px;
|
||||
border-radius: 16px;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#remote-screen-video-stream.inline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -44,7 +44,9 @@ function toggleSpeakerManually() {
|
||||
}
|
||||
function toggleVideoManually() {
|
||||
if (activeCall) {
|
||||
const apiCall = { command: { type: "media", media: CallMediaType.Video, enable: activeCall.localMediaSources.camera != true } };
|
||||
const apiCall = {
|
||||
command: { type: "media", source: CallMediaSource.Camera, enable: activeCall.localMediaSources.camera != true },
|
||||
};
|
||||
reactOnMessageFromServer(apiCall);
|
||||
processCommand(apiCall).then(() => {
|
||||
var _a;
|
||||
@@ -82,7 +84,7 @@ function reactOnMessageFromServer(msg) {
|
||||
document.getElementById("info-block").className = msg.command.media;
|
||||
break;
|
||||
case "media":
|
||||
const className = (msg.command.media == CallMediaType.Video && msg.command.enable) ||
|
||||
const className = (msg.command.source == CallMediaSource.Camera && msg.command.enable) ||
|
||||
(activeCall === null || activeCall === void 0 ? void 0 : activeCall.peerMediaSources.camera) ||
|
||||
(activeCall === null || activeCall === void 0 ? void 0 : activeCall.peerMediaSources.screenVideo)
|
||||
? "video"
|
||||
|
||||
+10
-7
@@ -34,14 +34,14 @@ actual fun ActiveCallView() {
|
||||
val callRh = call.remoteHostId
|
||||
when (val r = apiMsg.resp) {
|
||||
is WCallResponse.Capabilities -> withBGApi {
|
||||
val callType = CallType(call.localMedia, r.capabilities)
|
||||
val callType = CallType(call.initialCallType, r.capabilities)
|
||||
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
|
||||
chatModel.activeCall.value = call.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities)
|
||||
CallSoundsPlayer.startConnectingCallSound(scope)
|
||||
activeCallWaitDeliveryReceipt(scope)
|
||||
}
|
||||
is WCallResponse.Offer -> withBGApi {
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities)
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.initialCallType, r.capabilities)
|
||||
chatModel.activeCall.value = call.copy(callState = CallState.OfferSent, localCapabilities = r.capabilities)
|
||||
}
|
||||
is WCallResponse.Answer -> withBGApi {
|
||||
@@ -86,15 +86,18 @@ actual fun ActiveCallView() {
|
||||
is WCallCommand.Answer ->
|
||||
chatModel.activeCall.value = call.copy(callState = CallState.Negotiated)
|
||||
is WCallCommand.Media -> {
|
||||
when (cmd.media) {
|
||||
CallMediaType.Video -> chatModel.activeCall.value = call.copy(videoEnabled = cmd.enable)
|
||||
CallMediaType.Audio -> chatModel.activeCall.value = call.copy(audioEnabled = cmd.enable)
|
||||
val sources = call.localMediaSources
|
||||
when (cmd.source) {
|
||||
CallMediaSource.Mic -> chatModel.activeCall.value = call.copy(localMediaSources = sources.copy(mic = cmd.enable))
|
||||
CallMediaSource.Camera -> chatModel.activeCall.value = call.copy(localMediaSources = sources.copy(camera = cmd.enable))
|
||||
CallMediaSource.ScreenAudio -> chatModel.activeCall.value = call.copy(localMediaSources = sources.copy(screenAudio = cmd.enable))
|
||||
CallMediaSource.ScreenVideo -> chatModel.activeCall.value = call.copy(localMediaSources = sources.copy(screenVideo = cmd.enable))
|
||||
}
|
||||
}
|
||||
is WCallCommand.Camera -> {
|
||||
chatModel.activeCall.value = call.copy(localCamera = cmd.camera)
|
||||
if (!call.audioEnabled) {
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = false))
|
||||
if (!call.localMediaSources.mic) {
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = false))
|
||||
}
|
||||
}
|
||||
is WCallCommand.End ->
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@Composable
|
||||
actual fun ActiveCallInteractiveArea(call: Call) {
|
||||
val showMenu = remember { mutableStateOf(false) }
|
||||
val media = call.peerMediaSources ?: call.localMedia
|
||||
CompositionLocalProvider(
|
||||
LocalIndication provides NoIndication
|
||||
) {
|
||||
@@ -56,7 +55,7 @@ actual fun ActiveCallInteractiveArea(call: Call) {
|
||||
Modifier.padding().background(SimplexGreen, CircleShape).padding(4.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
) {
|
||||
if (media == CallMediaType.Video) {
|
||||
if (call.supportsVideo()) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_videocam_filled),
|
||||
stringResource(MR.strings.icon_descr_video_call),
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
<script src="../lz-string.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="visibility: hidden"
|
||||
onclick="javascript:toggleRemoteScreenVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
@@ -15,15 +24,6 @@
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="local-video-stream"
|
||||
class="inline"
|
||||
@@ -39,7 +39,7 @@
|
||||
muted
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
></video>
|
||||
</body>
|
||||
<footer>
|
||||
|
||||
@@ -12,6 +12,17 @@ body {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#remote-video-stream.collapsed {
|
||||
position: absolute;
|
||||
max-width: 30%;
|
||||
max-height: 30%;
|
||||
object-fit: cover;
|
||||
margin: 16px;
|
||||
border-radius: 16px;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#remote-screen-video-stream.inline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -134,7 +134,7 @@ interface WCallIceCandidates extends IWCallCommand, IWCallResponse {
|
||||
|
||||
interface WCEnableMedia extends IWCallCommand {
|
||||
type: "media"
|
||||
media: CallMediaType
|
||||
source: CallMediaSource
|
||||
enable: boolean
|
||||
}
|
||||
|
||||
@@ -377,7 +377,15 @@ const processCommand = (function () {
|
||||
const remoteStream = new MediaStream()
|
||||
const remoteScreenStream = new MediaStream()
|
||||
const localCamera = VideoCamera.User
|
||||
const localStream = await getLocalMediaStream(mediaType, localCamera)
|
||||
let localStream: MediaStream
|
||||
try {
|
||||
localStream = await getLocalMediaStream(mediaType, localCamera)
|
||||
} catch (e) {
|
||||
if (isDesktop) {
|
||||
window.alert("Permissions denied. Please, allow mic and video to make the call working and repeat again.")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
const localScreenStream = new MediaStream()
|
||||
if (isDesktop) {
|
||||
localStream
|
||||
@@ -596,11 +604,11 @@ const processCommand = (function () {
|
||||
case "media":
|
||||
if (!activeCall) {
|
||||
resp = {type: "error", message: "media: call not started"}
|
||||
} else if (!activeCall.cameraTrackWasSetBefore && command.media == CallMediaType.Video && command.enable) {
|
||||
} else if (!activeCall.cameraTrackWasSetBefore && command.source == CallMediaSource.Camera && command.enable) {
|
||||
await startSendingCamera(activeCall, activeCall.localCamera)
|
||||
resp = {type: "ok"}
|
||||
} else {
|
||||
enableMedia(activeCall.localStream, command.media, command.enable)
|
||||
enableMedia(activeCall.localStream, command.source, command.enable)
|
||||
resp = {type: "ok"}
|
||||
}
|
||||
break
|
||||
@@ -902,7 +910,7 @@ const processCommand = (function () {
|
||||
// videos.localScreen.pause()
|
||||
// videos.localScreen.srcObject = call.localScreenStream
|
||||
videos.localScreen.play()
|
||||
videos.localScreen.style.display = "block"
|
||||
videos.localScreen.style.visibility = "visible"
|
||||
} else {
|
||||
pc.getTransceivers().forEach((elem) => {
|
||||
const source = mediaSourceFromTransceiverMid(elem.mid)
|
||||
@@ -912,7 +920,7 @@ const processCommand = (function () {
|
||||
})
|
||||
for (const t of call.localScreenStream.getTracks()) t.stop()
|
||||
for (const t of call.localScreenStream.getTracks()) call.localScreenStream.removeTrack(t)
|
||||
videos.localScreen.style.display = "none"
|
||||
videos.localScreen.style.visibility = "hidden"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,53 +1010,57 @@ const processCommand = (function () {
|
||||
function onMediaMuteUnmute(transceiverMid: string | null, mute: boolean) {
|
||||
const videos = getVideoElements()
|
||||
if (!videos) throw Error("no video elements")
|
||||
if (activeCall) {
|
||||
const source = mediaSourceFromTransceiverMid(transceiverMid)
|
||||
console.log("LALAL ON MUTE/UNMUTE", mute, source, transceiverMid)
|
||||
const sources = activeCall.peerMediaSources
|
||||
if (source == CallMediaSource.Mic && activeCall.peerMediaSources.mic == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.mic = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.Camera && activeCall.peerMediaSources.camera == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.camera = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
videos.remote.style.display = !mute ? "block" : "none"
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.ScreenAudio && activeCall.peerMediaSources.screenAudio == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.screenAudio = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.ScreenVideo && activeCall.peerMediaSources.screenVideo == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.screenVideo = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
videos.remoteScreen.style.display = !mute ? "block" : "none"
|
||||
sendMessageToNative({resp: resp})
|
||||
if (!activeCall) return
|
||||
const source = mediaSourceFromTransceiverMid(transceiverMid)
|
||||
console.log("LALAL ON MUTE/UNMUTE", mute, source, transceiverMid)
|
||||
const sources = activeCall.peerMediaSources
|
||||
if (source == CallMediaSource.Mic && activeCall.peerMediaSources.mic == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.mic = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.Camera && activeCall.peerMediaSources.camera == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.camera = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
videos.remote.style.visibility = !mute ? "visible" : "hidden"
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.ScreenAudio && activeCall.peerMediaSources.screenAudio == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Audio,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.screenAudio = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
sendMessageToNative({resp: resp})
|
||||
} else if (source == CallMediaSource.ScreenVideo && activeCall.peerMediaSources.screenVideo == mute) {
|
||||
const resp: WRPeerMedia = {
|
||||
type: "peerMedia",
|
||||
media: CallMediaType.Video,
|
||||
source: source,
|
||||
enabled: !mute,
|
||||
}
|
||||
sources.screenVideo = !mute
|
||||
activeCall.peerMediaSources = sources
|
||||
videos.remoteScreen.style.visibility = !mute ? "visible" : "hidden"
|
||||
sendMessageToNative({resp: resp})
|
||||
}
|
||||
if (activeCall.peerMediaSources.screenVideo) {
|
||||
videos.remote.className = "collapsed"
|
||||
} else {
|
||||
videos.remote.className = "inline"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1057,6 +1069,15 @@ const processCommand = (function () {
|
||||
return navigator.mediaDevices.getUserMedia(constraints)
|
||||
}
|
||||
|
||||
function getEmptyStream(mediaType: CallMediaType, pc: RTCPeerConnection): MediaStream {
|
||||
const stream = new MediaStream()
|
||||
pc.addTransceiver("audio", {streams: [stream]})
|
||||
if (mediaType == CallMediaType.Video) {
|
||||
pc.addTransceiver("video", {streams: [stream]})
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
function getLocalScreenCaptureStream(): Promise<MediaStream> {
|
||||
const constraints: any /* DisplayMediaStreamConstraints */ = {
|
||||
video: {
|
||||
@@ -1154,19 +1175,24 @@ const processCommand = (function () {
|
||||
// }
|
||||
// }
|
||||
|
||||
function enableMedia(s: MediaStream, media: CallMediaType, enable: boolean) {
|
||||
const tracks = media == CallMediaType.Video ? s.getVideoTracks() : s.getAudioTracks()
|
||||
function enableMedia(s: MediaStream, source: CallMediaSource, enable: boolean) {
|
||||
const tracks = source == CallMediaSource.Camera ? s.getVideoTracks() : s.getAudioTracks()
|
||||
for (const t of tracks)
|
||||
activeCall?.connection.getTransceivers().forEach((elem) => {
|
||||
if (enable) {
|
||||
t.enabled = true
|
||||
elem.sender.replaceTrack(t)
|
||||
} else {
|
||||
t.enabled = false
|
||||
elem.sender.replaceTrack(null)
|
||||
if (
|
||||
(t.kind == CallMediaType.Audio && mediaSourceFromTransceiverMid(elem.mid) == CallMediaSource.Mic) ||
|
||||
(t.kind == CallMediaType.Video && mediaSourceFromTransceiverMid(elem.mid) == CallMediaSource.Camera)
|
||||
) {
|
||||
if (enable) {
|
||||
t.enabled = true
|
||||
elem.sender.replaceTrack(t)
|
||||
} else {
|
||||
t.enabled = false
|
||||
elem.sender.replaceTrack(null)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (media == CallMediaType.Video && activeCall) {
|
||||
if (source == CallMediaSource.Camera && activeCall) {
|
||||
activeCall.localMediaSources.camera = enable
|
||||
}
|
||||
}
|
||||
@@ -1187,6 +1213,11 @@ function toggleRemoteVideoFitFill() {
|
||||
remote.style.objectFit = remote.style.objectFit != "contain" ? "contain" : "cover"
|
||||
}
|
||||
|
||||
function toggleRemoteScreenVideoFitFill() {
|
||||
const remoteScreen = document.getElementById("remote-screen-video-stream")!
|
||||
remoteScreen.style.objectFit = remoteScreen.style.objectFit != "contain" ? "contain" : "cover"
|
||||
}
|
||||
|
||||
function toggleMedia(s: MediaStream, media: CallMediaType): boolean {
|
||||
let res = false
|
||||
const tracks = media == CallMediaType.Video ? s.getVideoTracks() : s.getAudioTracks()
|
||||
|
||||
@@ -7,20 +7,20 @@
|
||||
<script src="/lz-string.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-screen-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
onclick="javascript:toggleRemoteScreenVideoFitFill()"
|
||||
></video>
|
||||
|
||||
<video
|
||||
id="remote-video-stream"
|
||||
class="inline"
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
onclick="javascript:toggleRemoteVideoFitFill()"
|
||||
></video>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
muted
|
||||
playsinline
|
||||
poster="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="
|
||||
style="display: none"
|
||||
style="visibility: hidden"
|
||||
></video>
|
||||
|
||||
<div id="progress"></div>
|
||||
@@ -50,19 +50,19 @@
|
||||
<img src="/desktop/images/ic_phone_in_talk.svg" />
|
||||
</div>
|
||||
<p id="manage-call">
|
||||
<button id="toggle-screen" style="display: none" onclick="javascript:toggleScreenManually()">
|
||||
<button id="toggle-screen" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleScreenManually()">
|
||||
<img src="/desktop/images/ic_screen_share.svg" />
|
||||
</button>
|
||||
<button id="toggle-audio" style="display: none" onclick="javascript:toggleAudioManually()">
|
||||
<button id="toggle-audio" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleAudioManually()">
|
||||
<img src="/desktop/images/ic_mic.svg" />
|
||||
</button>
|
||||
<button id="end-call" onclick="javascript:endCallManually()">
|
||||
<img src="/desktop/images/ic_call_end_filled.svg" />
|
||||
</button>
|
||||
<button id="toggle-speaker" style="display: none" onclick="javascript:toggleSpeakerManually()">
|
||||
<button id="toggle-speaker" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleSpeakerManually()">
|
||||
<img src="/desktop/images/ic_volume_up.svg" />
|
||||
</button>
|
||||
<button id="toggle-video" style="display: none" onclick="javascript:toggleVideoManually()">
|
||||
<button id="toggle-video" style="display: none; width: 44px; height: 44px" onclick="javascript:toggleVideoManually()">
|
||||
<img src="/desktop/images/ic_videocam_off.svg" />
|
||||
</button>
|
||||
</p>
|
||||
|
||||
@@ -12,6 +12,17 @@ body {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#remote-video-stream.collapsed {
|
||||
position: absolute;
|
||||
max-width: 20%;
|
||||
max-height: 20%;
|
||||
object-fit: cover;
|
||||
margin: 16px;
|
||||
border-radius: 16px;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#remote-screen-video-stream.inline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -51,7 +51,9 @@ function toggleSpeakerManually() {
|
||||
|
||||
function toggleVideoManually() {
|
||||
if (activeCall) {
|
||||
const apiCall: WVAPICall = {command: {type: "media", media: CallMediaType.Video, enable: activeCall.localMediaSources.camera != true}}
|
||||
const apiCall: WVAPICall = {
|
||||
command: {type: "media", source: CallMediaSource.Camera, enable: activeCall.localMediaSources.camera != true},
|
||||
}
|
||||
reactOnMessageFromServer(apiCall as any)
|
||||
processCommand(apiCall).then(() => {
|
||||
enableVideoIcon(activeCall?.localMediaSources?.camera == true)
|
||||
@@ -90,7 +92,7 @@ function reactOnMessageFromServer(msg: WVApiMessage) {
|
||||
break
|
||||
case "media":
|
||||
const className =
|
||||
(msg.command.media == CallMediaType.Video && msg.command.enable) ||
|
||||
(msg.command.source == CallMediaSource.Camera && msg.command.enable) ||
|
||||
activeCall?.peerMediaSources.camera ||
|
||||
activeCall?.peerMediaSources.screenVideo
|
||||
? "video"
|
||||
|
||||
Reference in New Issue
Block a user