Skip to content

Commit 2770d1f

Browse files
authored
Merge pull request #39 from zjc19891106/dev
HIM-19562
2 parents f7ce57b + 968398a commit 2770d1f

File tree

7 files changed

+154
-53
lines changed

7 files changed

+154
-53
lines changed

EaseCallUIKit.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'EaseCallUIKit'
11-
s.version = '4.17.0'
11+
s.version = '4.18.0'
1212
s.summary = 'A short description of EaseCallUIKit.'
1313

1414
# This description is used to generate tags and improve search results.
@@ -42,6 +42,6 @@ TODO: Add long description of the pod here.
4242
# s.public_header_files = 'Pod/Classes/**/*.h'
4343
s.frameworks = 'UIKit', 'Foundation', 'Combine', 'AudioToolbox', 'AVFoundation','AVKit', 'CoreMedia', 'CoreVideo', 'CoreGraphics'
4444

45-
s.dependency 'HyphenateChat','>= 4.17.0'
45+
s.dependency 'HyphenateChat','>= 4.18.0'
4646
s.dependency 'AgoraRtcEngine_iOS/RtcBasic', '~> 4.6.0'
4747
end

Sources/EaseCallUIKit/Classes/CoreService/Implements/CallKitManager+RTC.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ extension CallKitManager: CallActionService {
5656

5757
/// Set up local video capturing and rendering
5858
func setupLocalVideo() {
59-
let cameraConfig = AgoraCameraCapturerConfiguration()
60-
cameraConfig.cameraDirection = .front
61-
self.engine?.setCameraCapturerConfiguration(cameraConfig)
6259
self.engine?.enableVideo()
6360
self.engine?.enableAudio()
6461
if let call = self.callInfo {
@@ -604,29 +601,23 @@ extension CallKitManager: AgoraRtcEngineDelegate {
604601
controller.callView.micView.isHidden = true
605602
if self.isVideoExchanged {// If video is exchanged, update mic view visibility
606603
if muted {
607-
controller.micView.isHidden = false
608604
controller.floatView.updateAudioState(!muted)
609605
} else {
610-
controller.micView.isHidden = true
611606
controller.floatView.updateAudioState(muted)
612607
}
613608
} else {
614-
controller.micView.isHidden = true
615609
controller.floatView.updateAudioState(muted)
616610
}
617611
} else {// If current controller is not Call1v1VideoViewController
618612
if let controller = self.callVC as? Call1v1VideoViewController {
619613
controller.callView.micView.isHidden = true
620614
if self.isVideoExchanged {// If video is exchanged, update mic view visibility and audio state
621615
if muted {
622-
controller.micView.isHidden = false
623616
controller.floatView.updateAudioState(!muted)
624617
} else {
625-
controller.micView.isHidden = true
626618
controller.floatView.updateAudioState(muted)
627619
}
628620
} else {// If video is not exchanged, hide mic view and update audio state
629-
controller.micView.isHidden = true
630621
controller.floatView.updateAudioState(muted)
631622
}
632623
}
@@ -748,6 +739,25 @@ extension CallKitManager: AgoraRtcEngineDelegate {
748739
extension CallKitManager: AgoraVideoFrameDelegate {
749740
public func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool {// This method is called when local video frame is captured.
750741
if let call = self.callInfo {
742+
// 处理群组通话预览(仅前台且当前显示的页面)
743+
if call.type == .groupCall {
744+
// 只处理当前正在显示的 CallMultiViewController
745+
if let controller = UIViewController.currentController as? CallMultiViewController {
746+
// 未连接状态且开启了摄像头预览
747+
if controller.isCameraPreviewEnabled, let previewView = controller.localPreviewView {
748+
if let pixelBuffer = videoFrame.pixelBuffer {
749+
previewView.renderVideoPixelBuffer(pixelBuffer: pixelBuffer, width: videoFrame.width, height: videoFrame.height)
750+
} else {
751+
previewView.renderFromVideoFrameData(videoData: videoFrame)
752+
}
753+
return true
754+
}
755+
}
756+
// 群组通话在后台或缩小时不处理预览,直接返回
757+
return true
758+
}
759+
760+
// 原有逻辑:处理1v1视频通话
751761
if call.type == .singleVideo {
752762
if let controller = UIViewController.currentController as? Call1v1VideoViewController {
753763
if let pixelBuffer = videoFrame.pixelBuffer {

Sources/EaseCallUIKit/Classes/CoreService/Implements/CallKitManager+Signaling.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,11 +1446,19 @@ extension CallKitManager: CallMessageService {
14461446
public func accept() {
14471447
AudioPlayerManager.shared.stopAudio()
14481448
if let call = self.callInfo {
1449-
if call.type == .singleVideo {
1450-
self.setupLocalVideo()
1451-
// self.enableLocalVideo(true)
1452-
} else {
1449+
switch call.type {
1450+
case .singleAudio:
14531451
self.enableLocalVideo(false)
1452+
case .singleVideo:
1453+
self.setupLocalVideo()
1454+
case .groupCall:
1455+
if let vc = UIViewController.currentController as? CallMultiViewController {
1456+
if vc.isCameraPreviewEnabled {
1457+
self.setupLocalVideo()
1458+
} else {
1459+
self.enableLocalVideo(false)
1460+
}
1461+
}
14541462
}
14551463
self.engine?.enableAudio()
14561464
self.enableLocalAudio(true)

Sources/EaseCallUIKit/Classes/CoreService/Implements/CallKitManager.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ public let CallKitVersion = "1.0.0"
158158
configuration.dimensions = CGSize(width: 1280, height: 720)
159159
configuration.frameRate = .fps30
160160
self.engine?.setVideoEncoderConfiguration(configuration)
161+
162+
let cameraConfig = AgoraCameraCapturerConfiguration()
163+
cameraConfig.cameraDirection = .front
164+
self.engine?.setCameraCapturerConfiguration(cameraConfig)
161165
for listener in self.listeners.allObjects {
162166
if let engine = self.engine {
163167
listener.onRtcEngineCreated?(engine: engine)

Sources/EaseCallUIKit/Classes/UI/Controllers/Call1v1VideoViewController.swift

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ open class Call1v1VideoViewController: UIViewController {
8383
return drag
8484
}()
8585

86-
public lazy var micView: UIImageView = {
87-
UIImageView(frame: CGRect(x: 18, y: self.bottomView.frame.minY - 14, width: 14, height: 14)).image(UIImage(named: "mic_off", in: .callBundle, with: nil)).isUserInteractionEnabled(false).tag(1002)
88-
}()
89-
9086
public private(set) var role: CallRole = .caller
9187

9288
/// Picture-in-Picture controller
@@ -193,9 +189,8 @@ open class Call1v1VideoViewController: UIViewController {
193189

194190
// 新增:集中管理视图层级
195191
private func setupViews() {
196-
// 确保视图层级正确
197-
self.micView.isHidden = true
198-
self.view.addSubViews([self.background, self.navigationBar, self.bottomView, self.micView,self.navigationBlur])
192+
193+
self.view.addSubViews([self.background, self.navigationBar, self.bottomView,self.navigationBlur])
199194
self.background.addSubViews([self.callView, self.floatView])
200195
self.navigationBlur.image = UIImage(named: "mask", in: .callBundle, with: nil)
201196
// 确保floatView在最上层
@@ -217,9 +212,7 @@ open class Call1v1VideoViewController: UIViewController {
217212
self.background.bringSubviewToFront(self.floatView)
218213
self.floatView.isUserInteractionEnabled = true
219214
self.callView.isUserInteractionEnabled = false
220-
self.floatView.micView.isHidden = false
221215
self.callView.micView.isHidden = true
222-
self.micView.isHidden = true
223216
self.floatView.updateAudioState(self.floatView.isAudioMuted)
224217
self.floatView.blurEffectView.isHidden = true
225218
self.callView.blurEffectView.isHidden = false
@@ -241,7 +234,6 @@ open class Call1v1VideoViewController: UIViewController {
241234
self.callView.isUserInteractionEnabled = true
242235
self.floatView.micView.isHidden = true
243236
self.callView.micView.isHidden = true
244-
self.micView.isHidden = !self.floatView.isAudioMuted
245237
self.floatView.blurEffectView.isHidden = false
246238
self.callView.blurEffectView.isHidden = true
247239
}
@@ -273,6 +265,7 @@ open class Call1v1VideoViewController: UIViewController {
273265
}
274266

275267
@objc open func bottomClick(type: CallButtonType) {
268+
print("bottomClick type:\(type.rawValue)")
276269
switch type {
277270
case .mic_on: CallKitManager.shared.enableLocalAudio(true)
278271
case .mic_off: CallKitManager.shared.enableLocalAudio(false)
@@ -490,13 +483,11 @@ open class Call1v1VideoViewController: UIViewController {
490483
UIView.animate(withDuration: 0.3) {
491484
self.navigationBar.alpha = 1
492485
self.bottomView.alpha = 1
493-
self.micView.frame = CGRect(x: 18, y: self.bottomView.frame.minY - 14 , width: 14, height: 14)
494486
}
495487
} else {
496488
UIView.animate(withDuration: 0.3) {
497489
self.navigationBar.alpha = 0
498490
self.bottomView.alpha = 0
499-
self.micView.frame = CGRect(x: 18, y: ScreenHeight - BottomBarHeight - 14 - 12, width: 14, height: 14)
500491
}
501492
}
502493
}

Sources/EaseCallUIKit/Classes/UI/Controllers/CallMultiViewController.swift

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@ open class CallMultiViewController: UIViewController {
6262
}
6363

6464
public private(set) var role: CallRole = .caller
65-
65+
66+
// 本地摄像头预览视图(未接听状态下使用)
67+
public var localPreviewView: PixelBufferRenderView?
68+
69+
// 跟踪摄像头状态
70+
public var isCameraPreviewEnabled: Bool = false
71+
6672
@objc public init(role: CallRole) {
6773
self.role = role
6874
super.init(nibName: nil, bundle: nil)
@@ -88,7 +94,8 @@ open class CallMultiViewController: UIViewController {
8894
} else {
8995
self.bottomView.isCallConnected = false
9096
}
91-
self.bottomView.updateButtonSelectedStatus(selectedIndex: 3)
97+
// 初始化时不触发回调,只更新按钮状态
98+
self.bottomView.updateButtonSelectedStatus(selectedIndex: 3, triggerCallback: false)
9299
self.callView.isHidden = !state
93100
// Do any additional setup after loading the view.
94101
self.setupNavigationState()
@@ -99,7 +106,6 @@ open class CallMultiViewController: UIViewController {
99106
self.bottomView.didTapButton = { [weak self] in
100107
self?.bottomClick(type: $0)
101108
}
102-
CallKitManager.shared.enableLocalVideo(false)
103109
}
104110

105111
func updateNavigationBar() {
@@ -122,6 +128,11 @@ open class CallMultiViewController: UIViewController {
122128

123129
func updateBottomState() {
124130
if self.connected {
131+
// 连接成功后移除预览视图
132+
self.removeLocalPreview()
133+
self.isCameraPreviewEnabled = false
134+
135+
// 原有逻辑
125136
self.callView.isHidden = !self.connected
126137
self.bottomView.animateToExpandedState()
127138
self.bottomView.isCallConnected = true
@@ -168,7 +179,6 @@ open class CallMultiViewController: UIViewController {
168179
}
169180

170181
@objc open func bottomClick(type: CallButtonType) {
171-
172182
switch type {
173183
case .mic_on:
174184
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
@@ -189,22 +199,39 @@ open class CallMultiViewController: UIViewController {
189199
case .flip_back: CallKitManager.shared.switchCamera()
190200
case .flip_front: CallKitManager.shared.switchCamera()
191201
case .camera_on:
192-
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
193-
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
194-
return
202+
if !self.connected {
203+
// 未连接状态:显示全屏预览
204+
self.setupLocalPreview()
205+
CallKitManager.shared.setupLocalVideo()
206+
CallKitManager.shared.enableLocalVideo(true)
207+
self.isCameraPreviewEnabled = true
208+
} else {
209+
// 已连接状态:正常处理(保持现有逻辑)
210+
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
211+
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
212+
return
213+
}
214+
CallKitManager.shared.setupLocalVideo()
215+
CallKitManager.shared.enableLocalVideo(true)
216+
item.videoMuted = false
217+
canvas.updateItem(item)
195218
}
196-
CallKitManager.shared.setupLocalVideo()
197-
CallKitManager.shared.enableLocalVideo(true)
198-
item.videoMuted = false
199-
canvas.updateItem(item)
200219
case .camera_off:
201-
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
202-
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
203-
return
220+
if !self.connected {
221+
// 未连接状态:移除预览
222+
self.removeLocalPreview()
223+
CallKitManager.shared.enableLocalVideo(false)
224+
self.isCameraPreviewEnabled = false
225+
} else {
226+
// 已连接状态:正常处理(保持现有逻辑)
227+
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
228+
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
229+
return
230+
}
231+
CallKitManager.shared.enableLocalVideo(false)
232+
item.videoMuted = true
233+
canvas.updateItem(item)
204234
}
205-
CallKitManager.shared.enableLocalVideo(false)
206-
item.videoMuted = true
207-
canvas.updateItem(item)
208235
case .speaker_on:
209236
CallKitManager.shared.turnSpeakerOn(on: true)
210237
case .speaker_off:
@@ -214,6 +241,13 @@ open class CallMultiViewController: UIViewController {
214241
self.dismiss(animated: true, completion: nil)
215242
CallKitManager.shared.hangup()
216243
case .accept:
244+
// 保存摄像头开启状态(在移除预览前检查)
245+
let wasCameraOn = self.isCameraPreviewEnabled
246+
247+
// 接受通话前先移除预览视图
248+
self.removeLocalPreview()
249+
self.isCameraPreviewEnabled = false
250+
217251
if let call = CallKitManager.shared.callInfo {
218252
GlobalTimerManager.shared.registerListener(self, timerIdentify: "call-\(call.channelName)-answering-timer")
219253
GlobalTimerManager.shared.registerListener(CallKitManager.shared, timerIdentify: "call-\(call.channelName)-answering-timer")
@@ -228,6 +262,32 @@ open class CallMultiViewController: UIViewController {
228262
CallKitManager.shared.accept()
229263
}
230264
self.callView.isHidden = false
265+
266+
// 如果接听前摄像头是开启的,需要同步状态到 MultiPersonCallView
267+
if wasCameraOn {
268+
consoleLogInfo("CallMultiViewController: Camera was on before accept, restoring state...", type: .debug)
269+
270+
// accept() 方法会调用 enableLocalVideo(false),需要立即覆盖
271+
CallKitManager.shared.setupLocalVideo()
272+
CallKitManager.shared.enableLocalVideo(true)
273+
274+
// 延迟更新,确保 canvas 已创建并同步状态
275+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
276+
guard let self = self else { return }
277+
if let currentUserId = ChatClient.shared().currentUsername {
278+
if let item = CallKitManager.shared.itemsCache[currentUserId],
279+
let canvas = CallKitManager.shared.canvasCache[currentUserId] {
280+
item.videoMuted = false
281+
canvas.updateItem(item)
282+
// 触发 MultiPersonCallView 更新
283+
self.callView.updateWithItems()
284+
consoleLogInfo("CallMultiViewController: Camera state synced - userId=\(currentUserId), videoMuted=false", type: .debug)
285+
} else {
286+
consoleLogInfo("CallMultiViewController: Failed to sync camera state - item or canvas not found for userId=\(currentUserId)", type: .error)
287+
}
288+
}
289+
}
290+
}
231291
case .end:
232292
if let call = CallKitManager.shared.callInfo {
233293
GlobalTimerManager.shared.removeListener(self, timerIdentify: "call-\(call.channelName)-answering-timer")
@@ -272,6 +332,9 @@ open class CallMultiViewController: UIViewController {
272332
}
273333

274334
open override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
335+
// 页面关闭时清理预览
336+
self.removeLocalPreview()
337+
self.isCameraPreviewEnabled = false
275338
super.dismiss(animated: flag, completion: completion)
276339
}
277340

@@ -353,6 +416,28 @@ open class CallMultiViewController: UIViewController {
353416
}
354417
}
355418
}
419+
420+
// 设置本地摄像头预览(全屏)
421+
private func setupLocalPreview() {
422+
guard localPreviewView == nil else { return }
423+
424+
let previewView = PixelBufferRenderView(frame: self.view.bounds)
425+
previewView.backgroundColor = .clear
426+
previewView.userId = ChatClient.shared().currentUsername ?? ""
427+
previewView.dragEnable = false
428+
previewView.tag = 9999 // 特殊标记
429+
430+
// 插入到背景和 navigationBar 之间
431+
self.view.insertSubview(previewView, aboveSubview: self.background)
432+
433+
self.localPreviewView = previewView
434+
}
435+
436+
// 移除本地摄像头预览
437+
private func removeLocalPreview() {
438+
localPreviewView?.removeFromSuperview()
439+
localPreviewView = nil
440+
}
356441
}
357442

358443
extension CallMultiViewController: TimerServiceListener {

0 commit comments

Comments
 (0)