Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions EaseCallUIKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'EaseCallUIKit'
s.version = '4.17.0'
s.version = '4.18.0'
s.summary = 'A short description of EaseCallUIKit.'

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

s.dependency 'HyphenateChat','>= 4.17.0'
s.dependency 'HyphenateChat','>= 4.18.0'
s.dependency 'AgoraRtcEngine_iOS/RtcBasic', '~> 4.6.0'
end
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ extension CallKitManager: CallActionService {

/// Set up local video capturing and rendering
func setupLocalVideo() {
let cameraConfig = AgoraCameraCapturerConfiguration()
cameraConfig.cameraDirection = .front
self.engine?.setCameraCapturerConfiguration(cameraConfig)
self.engine?.enableVideo()
self.engine?.enableAudio()
if let call = self.callInfo {
Expand Down Expand Up @@ -604,29 +601,23 @@ extension CallKitManager: AgoraRtcEngineDelegate {
controller.callView.micView.isHidden = true
if self.isVideoExchanged {// If video is exchanged, update mic view visibility
if muted {
controller.micView.isHidden = false
controller.floatView.updateAudioState(!muted)
} else {
controller.micView.isHidden = true
controller.floatView.updateAudioState(muted)
}
} else {
controller.micView.isHidden = true
controller.floatView.updateAudioState(muted)
}
} else {// If current controller is not Call1v1VideoViewController
if let controller = self.callVC as? Call1v1VideoViewController {
controller.callView.micView.isHidden = true
if self.isVideoExchanged {// If video is exchanged, update mic view visibility and audio state
if muted {
controller.micView.isHidden = false
controller.floatView.updateAudioState(!muted)
} else {
controller.micView.isHidden = true
controller.floatView.updateAudioState(muted)
}
} else {// If video is not exchanged, hide mic view and update audio state
controller.micView.isHidden = true
controller.floatView.updateAudioState(muted)
}
}
Expand Down Expand Up @@ -748,6 +739,25 @@ extension CallKitManager: AgoraRtcEngineDelegate {
extension CallKitManager: AgoraVideoFrameDelegate {
public func onCapture(_ videoFrame: AgoraOutputVideoFrame, sourceType: AgoraVideoSourceType) -> Bool {// This method is called when local video frame is captured.
if let call = self.callInfo {
// 处理群组通话预览(仅前台且当前显示的页面)
if call.type == .groupCall {
// 只处理当前正在显示的 CallMultiViewController
if let controller = UIViewController.currentController as? CallMultiViewController {
// 未连接状态且开启了摄像头预览
if controller.isCameraPreviewEnabled, let previewView = controller.localPreviewView {
if let pixelBuffer = videoFrame.pixelBuffer {
previewView.renderVideoPixelBuffer(pixelBuffer: pixelBuffer, width: videoFrame.width, height: videoFrame.height)
} else {
previewView.renderFromVideoFrameData(videoData: videoFrame)
}
return true
}
}
// 群组通话在后台或缩小时不处理预览,直接返回
return true
}

// 原有逻辑:处理1v1视频通话
if call.type == .singleVideo {
if let controller = UIViewController.currentController as? Call1v1VideoViewController {
if let pixelBuffer = videoFrame.pixelBuffer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1446,11 +1446,19 @@ extension CallKitManager: CallMessageService {
public func accept() {
AudioPlayerManager.shared.stopAudio()
if let call = self.callInfo {
if call.type == .singleVideo {
self.setupLocalVideo()
// self.enableLocalVideo(true)
} else {
switch call.type {
case .singleAudio:
self.enableLocalVideo(false)
case .singleVideo:
self.setupLocalVideo()
case .groupCall:
if let vc = UIViewController.currentController as? CallMultiViewController {
if vc.isCameraPreviewEnabled {
self.setupLocalVideo()
} else {
self.enableLocalVideo(false)
}
}
}
self.engine?.enableAudio()
self.enableLocalAudio(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ public let CallKitVersion = "1.0.0"
configuration.dimensions = CGSize(width: 1280, height: 720)
configuration.frameRate = .fps30
self.engine?.setVideoEncoderConfiguration(configuration)

let cameraConfig = AgoraCameraCapturerConfiguration()
cameraConfig.cameraDirection = .front
self.engine?.setCameraCapturerConfiguration(cameraConfig)
for listener in self.listeners.allObjects {
if let engine = self.engine {
listener.onRtcEngineCreated?(engine: engine)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ open class Call1v1VideoViewController: UIViewController {
return drag
}()

public lazy var micView: UIImageView = {
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)
}()

public private(set) var role: CallRole = .caller

/// Picture-in-Picture controller
Expand Down Expand Up @@ -193,9 +189,8 @@ open class Call1v1VideoViewController: UIViewController {

// 新增:集中管理视图层级
private func setupViews() {
// 确保视图层级正确
self.micView.isHidden = true
self.view.addSubViews([self.background, self.navigationBar, self.bottomView, self.micView,self.navigationBlur])

self.view.addSubViews([self.background, self.navigationBar, self.bottomView,self.navigationBlur])
self.background.addSubViews([self.callView, self.floatView])
self.navigationBlur.image = UIImage(named: "mask", in: .callBundle, with: nil)
// 确保floatView在最上层
Expand All @@ -217,9 +212,7 @@ open class Call1v1VideoViewController: UIViewController {
self.background.bringSubviewToFront(self.floatView)
self.floatView.isUserInteractionEnabled = true
self.callView.isUserInteractionEnabled = false
self.floatView.micView.isHidden = false
self.callView.micView.isHidden = true
self.micView.isHidden = true
self.floatView.updateAudioState(self.floatView.isAudioMuted)
self.floatView.blurEffectView.isHidden = true
self.callView.blurEffectView.isHidden = false
Expand All @@ -241,7 +234,6 @@ open class Call1v1VideoViewController: UIViewController {
self.callView.isUserInteractionEnabled = true
self.floatView.micView.isHidden = true
self.callView.micView.isHidden = true
self.micView.isHidden = !self.floatView.isAudioMuted
self.floatView.blurEffectView.isHidden = false
self.callView.blurEffectView.isHidden = true
}
Expand Down Expand Up @@ -273,6 +265,7 @@ open class Call1v1VideoViewController: UIViewController {
}

@objc open func bottomClick(type: CallButtonType) {
print("bottomClick type:\(type.rawValue)")
switch type {
case .mic_on: CallKitManager.shared.enableLocalAudio(true)
case .mic_off: CallKitManager.shared.enableLocalAudio(false)
Expand Down Expand Up @@ -490,13 +483,11 @@ open class Call1v1VideoViewController: UIViewController {
UIView.animate(withDuration: 0.3) {
self.navigationBar.alpha = 1
self.bottomView.alpha = 1
self.micView.frame = CGRect(x: 18, y: self.bottomView.frame.minY - 14 , width: 14, height: 14)
}
} else {
UIView.animate(withDuration: 0.3) {
self.navigationBar.alpha = 0
self.bottomView.alpha = 0
self.micView.frame = CGRect(x: 18, y: ScreenHeight - BottomBarHeight - 14 - 12, width: 14, height: 14)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ open class CallMultiViewController: UIViewController {
}

public private(set) var role: CallRole = .caller


// 本地摄像头预览视图(未接听状态下使用)
public var localPreviewView: PixelBufferRenderView?

// 跟踪摄像头状态
public var isCameraPreviewEnabled: Bool = false

@objc public init(role: CallRole) {
self.role = role
super.init(nibName: nil, bundle: nil)
Expand All @@ -88,7 +94,8 @@ open class CallMultiViewController: UIViewController {
} else {
self.bottomView.isCallConnected = false
}
self.bottomView.updateButtonSelectedStatus(selectedIndex: 3)
// 初始化时不触发回调,只更新按钮状态
self.bottomView.updateButtonSelectedStatus(selectedIndex: 3, triggerCallback: false)
self.callView.isHidden = !state
// Do any additional setup after loading the view.
self.setupNavigationState()
Expand All @@ -99,7 +106,6 @@ open class CallMultiViewController: UIViewController {
self.bottomView.didTapButton = { [weak self] in
self?.bottomClick(type: $0)
}
CallKitManager.shared.enableLocalVideo(false)
}

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

func updateBottomState() {
if self.connected {
// 连接成功后移除预览视图
self.removeLocalPreview()
self.isCameraPreviewEnabled = false

// 原有逻辑
self.callView.isHidden = !self.connected
self.bottomView.animateToExpandedState()
self.bottomView.isCallConnected = true
Expand Down Expand Up @@ -168,7 +179,6 @@ open class CallMultiViewController: UIViewController {
}

@objc open func bottomClick(type: CallButtonType) {

switch type {
case .mic_on:
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
Expand All @@ -189,22 +199,39 @@ open class CallMultiViewController: UIViewController {
case .flip_back: CallKitManager.shared.switchCamera()
case .flip_front: CallKitManager.shared.switchCamera()
case .camera_on:
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
return
if !self.connected {
// 未连接状态:显示全屏预览
self.setupLocalPreview()
CallKitManager.shared.setupLocalVideo()
CallKitManager.shared.enableLocalVideo(true)
self.isCameraPreviewEnabled = true
} else {
// 已连接状态:正常处理(保持现有逻辑)
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
return
}
CallKitManager.shared.setupLocalVideo()
CallKitManager.shared.enableLocalVideo(true)
item.videoMuted = false
canvas.updateItem(item)
}
CallKitManager.shared.setupLocalVideo()
CallKitManager.shared.enableLocalVideo(true)
item.videoMuted = false
canvas.updateItem(item)
case .camera_off:
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
return
if !self.connected {
// 未连接状态:移除预览
self.removeLocalPreview()
CallKitManager.shared.enableLocalVideo(false)
self.isCameraPreviewEnabled = false
} else {
// 已连接状态:正常处理(保持现有逻辑)
guard let currentUserId = ChatClient.shared().currentUsername,let item = CallKitManager.shared.itemsCache[currentUserId],let canvas = CallKitManager.shared.canvasCache[currentUserId] else {
consoleLogInfo("CallMultiViewController: Current user not found in items cache.", type: .error)
return
}
CallKitManager.shared.enableLocalVideo(false)
item.videoMuted = true
canvas.updateItem(item)
}
CallKitManager.shared.enableLocalVideo(false)
item.videoMuted = true
canvas.updateItem(item)
case .speaker_on:
CallKitManager.shared.turnSpeakerOn(on: true)
case .speaker_off:
Expand All @@ -214,6 +241,13 @@ open class CallMultiViewController: UIViewController {
self.dismiss(animated: true, completion: nil)
CallKitManager.shared.hangup()
case .accept:
// 保存摄像头开启状态(在移除预览前检查)
let wasCameraOn = self.isCameraPreviewEnabled

// 接受通话前先移除预览视图
self.removeLocalPreview()
self.isCameraPreviewEnabled = false

if let call = CallKitManager.shared.callInfo {
GlobalTimerManager.shared.registerListener(self, timerIdentify: "call-\(call.channelName)-answering-timer")
GlobalTimerManager.shared.registerListener(CallKitManager.shared, timerIdentify: "call-\(call.channelName)-answering-timer")
Expand All @@ -228,6 +262,32 @@ open class CallMultiViewController: UIViewController {
CallKitManager.shared.accept()
}
self.callView.isHidden = false

// 如果接听前摄像头是开启的,需要同步状态到 MultiPersonCallView
if wasCameraOn {
consoleLogInfo("CallMultiViewController: Camera was on before accept, restoring state...", type: .debug)

// accept() 方法会调用 enableLocalVideo(false),需要立即覆盖
CallKitManager.shared.setupLocalVideo()
CallKitManager.shared.enableLocalVideo(true)

// 延迟更新,确保 canvas 已创建并同步状态
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
guard let self = self else { return }
if let currentUserId = ChatClient.shared().currentUsername {
if let item = CallKitManager.shared.itemsCache[currentUserId],
let canvas = CallKitManager.shared.canvasCache[currentUserId] {
item.videoMuted = false
canvas.updateItem(item)
// 触发 MultiPersonCallView 更新
self.callView.updateWithItems()
consoleLogInfo("CallMultiViewController: Camera state synced - userId=\(currentUserId), videoMuted=false", type: .debug)
} else {
consoleLogInfo("CallMultiViewController: Failed to sync camera state - item or canvas not found for userId=\(currentUserId)", type: .error)
}
}
}
}
case .end:
if let call = CallKitManager.shared.callInfo {
GlobalTimerManager.shared.removeListener(self, timerIdentify: "call-\(call.channelName)-answering-timer")
Expand Down Expand Up @@ -272,6 +332,9 @@ open class CallMultiViewController: UIViewController {
}

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

Expand Down Expand Up @@ -353,6 +416,28 @@ open class CallMultiViewController: UIViewController {
}
}
}

// 设置本地摄像头预览(全屏)
private func setupLocalPreview() {
guard localPreviewView == nil else { return }

let previewView = PixelBufferRenderView(frame: self.view.bounds)
previewView.backgroundColor = .clear
previewView.userId = ChatClient.shared().currentUsername ?? ""
previewView.dragEnable = false
previewView.tag = 9999 // 特殊标记

// 插入到背景和 navigationBar 之间
self.view.insertSubview(previewView, aboveSubview: self.background)

self.localPreviewView = previewView
}

// 移除本地摄像头预览
private func removeLocalPreview() {
localPreviewView?.removeFromSuperview()
localPreviewView = nil
}
}

extension CallMultiViewController: TimerServiceListener {
Expand Down
Loading