diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index 479a71ec3..61504cbc5 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -111,6 +111,8 @@ final class CoreContext: ObservableObject { self.mCore.videoDisplayEnabled = true self.mCore.videoPreviewEnabled = false + self.mCore.fecEnabled = true + self.mCoreSuscriptions.insert(self.mCore.publisher?.onGlobalStateChanged?.postOnMainQueue { (cbVal: (core: Core, state: GlobalState, message: String)) in if cbVal.state == GlobalState.On { self.hasDefaultAccount = true diff --git a/Linphone/Ressources/linphonerc-default b/Linphone/Ressources/linphonerc-default index ce7ae7161..8126cf1cc 100644 --- a/Linphone/Ressources/linphonerc-default +++ b/Linphone/Ressources/linphonerc-default @@ -40,4 +40,7 @@ version_check_url_root=https://www.linphone.org/releases max_calls=10 conference_layout=1 +[fec] +fec_enabled=1 + ## End of default rc diff --git a/Linphone/TelecomManager/TelecomManager.swift b/Linphone/TelecomManager/TelecomManager.swift index 92d32421a..ed3994313 100644 --- a/Linphone/TelecomManager/TelecomManager.swift +++ b/Linphone/TelecomManager/TelecomManager.swift @@ -385,15 +385,25 @@ class TelecomManager: ObservableObject { if call.conference != nil { if call.conference!.activeSpeakerParticipantDevice != nil { let direction = call.conference?.activeSpeakerParticipantDevice!.getStreamCapability(streamType: StreamType.Video) - self.remoteConfVideo = direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + } else if call.conference!.participantList.first != nil + && call.conference!.participantList.first?.address != nil + && call.conference!.participantList.first!.address!.clone()!.equal(address2: (call.conference!.me?.address)!) { + let direction = call.conference!.participantDeviceList.first!.getStreamCapability(streamType: StreamType.Video) + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + } else if call.conference!.participantList.last != nil + && call.conference!.participantList.last?.address != nil { + let direction = call.conference!.participantDeviceList.last!.getStreamCapability(streamType: StreamType.Video) + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly } else { - self.remoteConfVideo = true + self.remoteConfVideo = false } + } else { self.remoteConfVideo = false DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.remoteConfVideo = call.currentParams!.videoEnabled && call.currentParams!.videoDirection == MediaDirection.SendRecv || call.currentParams!.videoDirection == MediaDirection.SendOnly + self.remoteConfVideo = call.currentParams!.videoEnabled && call.currentParams!.videoDirection == .SendRecv || call.currentParams!.videoDirection == .SendOnly } } diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index 2e52f23f4..90d1e08b0 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -283,6 +283,7 @@ struct CallView: View { VStack(spacing: 0) { Button(action: { optionsChangeLayout = 1 + callViewModel.toggleVideoMode(isAudioOnlyMode: false) changeLayoutSheet = false }, label: { HStack { @@ -312,6 +313,7 @@ struct CallView: View { Button(action: { optionsChangeLayout = 2 + callViewModel.toggleVideoMode(isAudioOnlyMode: false) changeLayoutSheet = false }, label: { HStack { @@ -339,6 +341,10 @@ struct CallView: View { Button(action: { optionsChangeLayout = 3 + if callViewModel.videoDisplayed { + callViewModel.displayMyVideo() + } + callViewModel.toggleVideoMode(isAudioOnlyMode: true) changeLayoutSheet = false }, label: { HStack { @@ -509,6 +515,10 @@ struct CallView: View { currentOffset = (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) pointingUp = -(((currentOffset - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78)) / ((maxBottomSheetHeight * geometry.size.height) - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78))) - 0.5) * 2 } + .onChange(of: optionsChangeLayout) { optionsChangeLayoutValue in + currentOffset = (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) + pointingUp = -(((currentOffset - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78)) / ((maxBottomSheetHeight * geometry.size.height) - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78))) - 0.5) * 2 + } .edgesIgnoringSafeArea(.bottom) } } @@ -674,8 +684,11 @@ struct CallView: View { ) } } else if callViewModel.isConference && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil { + let heightValue = (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom) if optionsChangeLayout == 1 && callViewModel.participantList.count <= 5 { - mosaicMode(geometry: geometry, height: (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom)) + mosaicMode(geometry: geometry, height: heightValue) + } else if optionsChangeLayout == 3 { + audioOnlyMode(geometry: geometry, height: heightValue) } else { activeSpeakerMode(geometry: geometry) } @@ -1645,6 +1658,103 @@ struct CallView: View { } } + func audioOnlyMode(geometry: GeometryProxy, height: Double) -> some View { + VStack { + let layout = [ + GridItem(.fixed((geometry.size.width/2)-10)), + GridItem(.fixed((geometry.size.width/2)-10)) + ] + ScrollView { + LazyVGrid(columns: layout) { + if callViewModel.myParticipantModel != nil { + HStack { + Avatar(contactAvatarModel: callViewModel.myParticipantModel!.avatarModel, avatarSize: 50, hidePresence: true) + + Text(callViewModel.myParticipantModel!.name) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundStyle(Color.white) + .default_text_style_500(styleSize: 14) + .lineLimit(1) + .padding(.horizontal, 10) + + if callViewModel.myParticipantModel!.isMuted { + HStack(alignment: .center) { + Image("microphone-slash") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 20, height: 20) + } + .padding(2) + .background(.white) + .cornerRadius(40) + } + + if callViewModel.myParticipantModel!.onPause { + Image("pause") + .renderingMode(.template) + .resizable() + .foregroundStyle(.white) + .frame(width: 25, height: 25) + } + } + .frame(height: 80) + .padding(.all, 10) + .background(Color.gray600) + .overlay( + RoundedRectangle(cornerRadius: 20) + .stroke(callViewModel.myParticipantModel!.isSpeaking ? .white : Color.gray600, lineWidth: 4) + ) + .cornerRadius(20) + } + + ForEach(0.. some View { GeometryReader { _ in @@ -1686,7 +1796,12 @@ struct CallView: View { Spacer() Button { - callViewModel.displayMyVideo() + if optionsChangeLayout == 3 { + optionsChangeLayout = 2 + callViewModel.toggleVideoMode(isAudioOnlyMode: false) + } else { + callViewModel.displayMyVideo() + } } label: { HStack { Image(callViewModel.videoDisplayed ? "video-camera" : "video-camera-slash") diff --git a/Linphone/UI/Call/ViewModel/CallViewModel.swift b/Linphone/UI/Call/ViewModel/CallViewModel.swift index 72a08548a..16629dafb 100644 --- a/Linphone/UI/Call/ViewModel/CallViewModel.swift +++ b/Linphone/UI/Call/ViewModel/CallViewModel.swift @@ -647,6 +647,22 @@ class CallViewModel: ObservableObject { } } + func toggleVideoMode(isAudioOnlyMode: Bool) { + coreContext.doOnCoreQueue { core in + if self.currentCall != nil { + do { + let params = try core.createCallParams(call: self.currentCall) + + params.videoEnabled = !isAudioOnlyMode + + try self.currentCall!.update(params: params) + } catch { + + } + } + } + } + func switchCamera() { coreContext.doOnCoreQueue { core in let currentDevice = core.videoDevice