Skip to content

Conversation

@youn9k
Copy link
Member

@youn9k youn9k commented Nov 29, 2024

🤔 배경

  • p2p 연결이 확정적으로 이루어지지 않고 3명 이상은 연결이 안되던 점을 해결해야 했습니다.

📃 작업 내역

  • WebRTCService 객체를 ConnectionClient 마다 하나씩 생성 후 주입
  • Offer/Answer의 SDP Publisher 분리
  • SessionDescription Model 변경

✅ 리뷰 노트

  1. WebRTCService를 모든 ConnectionClient 가 공유하던 부분 수정
  • 기존에는 하나의 WebRTCService를 모든 ConnectionClient가 공유하고 있었습니다. 그런데 peerConnection은 WebRTCService 당 하나만 존재해서 결국 기기 하나만 연결될 수 있는 구조였습니다. 그래서 ConnectionClient마다 별개의 WebRTCService를 주입해줬습니다.
makeConnectionClient(
    signalingService: signalingService,
    webRTCService: makeWebRTCService(
        iceServers: stunServers
    )
)
  1. Publisher 분리
  • 원래는 didReceiveSdpPublisher를 이용해서 offer 들어온 SDP와 answer 들어온 sdp를 동시에 처리해주고 있었습니다. 그런데 이렇게 하다보니 조건문 처리도 복잡해지고 sdp 관리도 힘들어져서 offer 전용 Publisher와 answer 전용 Publisher를 분리해줬습니다.
self.signalingService.didReceiveOfferSdpPublisher
  .filter { [weak self] _ in self?.remoteUserInfo != nil }
  .sink { [weak self] sdpMessage in
      guard let self else { return }
      let remoteSDP = sdpMessage.rtcSessionDescription
          
      // MARK: remoteDescription이 있으면 이미 연결된 클라이언트
      guard self.webRTCService.peerConnection.remoteDescription == nil else { } 
      guard let userInfo = self.remoteUserInfo else {  }
      guard userInfo.id == sdpMessage.offerID else {  }
      guard self.webRTCService.peerConnection.localDescription == nil else {  }
          
      self.webRTCService.set(remoteSdp: remoteSDP) {  }
  }.store(in: &cancellables)
  
self.signalingService.didReceiveAnswerSdpPublisher
  .filter { [weak self] _ in self?.remoteUserInfo != nil }
  .sink { [weak self] sdpMessage in
      guard let self else { return }
      let remoteSDP = sdpMessage.rtcSessionDescription

      guard let userInfo = remoteUserInfo else {  }
      guard userInfo.id == sdpMessage.answerID else {  }
      guard self.webRTCService.peerConnection.localDescription != nil else {  }
      guard self.webRTCService.peerConnection.remoteDescription == nil else {  }
    
      self.webRTCService.set(remoteSdp: remoteSDP) {   }
      }
  }.store(in: &cancellables)
  1. 변경된 Publisher로직을 위해 서버와 통신하는 데이터 모델을 수정
  • 새로운 publisher의 로직을 위해 Answer를 보내는 사람의 ID가 필요해져서 answerID라는 변수를 추가했습니다.
  • 기존의 userID 변수명은 헷갈리기 때문에 offerID로 변경했습니다.
public struct SessionDescriptionMessage: Codable {
    public let sdp: String
    public let type: SdpType
    public let roomID: String // MARK: 참가하려는 방의 ID
    public let offerID: String // MARK: Offer를 보내는 사람의 ID
    public let answerID: String? // MARK: Answer를 보내는 사람의 ID
}

🎨 스크린샷

iPhone SE(2세대) iPhone 14 iPhone 16 Pro Max
스샷 스샷 스샷

🚀 테스트 방법

현재 2명의 연결은 안정적으로 되는 것을 확인했으나 다수 연결은 직접 테스트해보지 못했습니다.
연결이 될 경우 WaitingRoom에서 서로의 음성이 연결됩니다. 영상은 바인딩 오류 문제 확인하여 수정이 필요합니다.

Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
@youn9k youn9k added the 🔧 fix 버그 수정 label Nov 29, 2024
@youn9k youn9k requested review from 0Hooni and hsw1920 November 29, 2024 11:49
@youn9k youn9k marked this pull request as draft November 29, 2024 11:49
@youn9k youn9k changed the title [FIX/#130] 여러 사용자가 연결되지 않는 문제를 수정합니다. [FIX/#130] 2. 여러 사용자가 연결되지 않는 문제를 수정합니다. Nov 29, 2024
@0Hooni
Copy link
Collaborator

0Hooni commented Nov 29, 2024

떳다 ㄷㄷㄷ

Kiyoung-Kim-57 and others added 11 commits November 29, 2024 21:04
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
- Offer 보낼 때 SessionDescription에 answerID 정보를 같이 담아서 보낼 수 있도록 모델 수정
- Offer 보낼 때 answerID 정보 포함해서 보냄

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
answerID는 Answer를 보내는 사람의 ID입니다.
Offer를 보내고 Answer가 왔을 때 특정 Client에만 remoteSDP를 저장해야합니다.
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
- OfferSdpPublisher, AnswerSdpPublisher 의 로직을 수정했습니다.

Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
UI변경을 메인스레드에서 하기 위해 receive RunLoop.main을 달아두었습니다.
Co-Authored-By: Kiyoung <121777185+Kiyoung-Kim-57@users.noreply.github.com>
@youn9k youn9k marked this pull request as ready for review November 30, 2024 05:29
Copy link
Collaborator

@hsw1920 hsw1920 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!!! 드디어..!

Comment on lines +87 to +114
self.signalingService.didReceiveOfferSdpPublisher
.filter { [weak self] _ in self?.remoteUserInfo != nil }
.sink { [weak self] sdpMessage in
guard let self else { return }
let remoteSDP = sdpMessage.rtcSessionDescription

PTGDataLogger.log("didReceiveRemoteSdpPublisher sink!! \(remoteSDP)")

// MARK: remoteDescription이 있으면 이미 연결된 클라이언트
guard self.webRTCService.peerConnection.remoteDescription == nil else {
PTGDataLogger.log("remoteSDP가 이미 있어요!")
return
}
PTGDataLogger.log("remoteSDP가 없어요! remoteSDP 저장하기 직전")
guard let userInfo = self.remoteUserInfo else {
PTGDataLogger.log("answer를 받을 remote User가 없어요!! 비상!!!")
return
}

guard userInfo.id == sdpMessage.offerID else {
PTGDataLogger.log("Offer를 보낸 유저가 일치하지 않습니다.")
return
}

guard self.webRTCService.peerConnection.localDescription == nil else {
PTGDataLogger.log("localSDP가 이미 있어요!")
return
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 set을 시키지 않기 위함이 주 목적이라면, guard 하는 부분은 따로 private 메서드로 빼주는 건 어떨까요?

Comment on lines +133 to +156
self.signalingService.didReceiveAnswerSdpPublisher
.filter { [weak self] _ in self?.remoteUserInfo != nil }
.sink { [weak self] sdpMessage in
guard let self else { return }
let remoteSDP = sdpMessage.rtcSessionDescription

guard let userInfo = remoteUserInfo else {
PTGDataLogger.log("UserInfo가 없어요")
return
}

guard userInfo.id == sdpMessage.answerID else {
return
}

guard self.webRTCService.peerConnection.localDescription != nil else {
PTGDataLogger.log("localDescription이 없어요")
return
}

guard self.webRTCService.peerConnection.remoteDescription == nil else {
PTGDataLogger.log("remoteDescription이 있어요")
return
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 마찬가지로 분리하면 가독성이 좋을 것 같아요

Comment on lines +116 to 128
self.webRTCService.set(remoteSdp: remoteSDP) { error in
PTGDataLogger.log("remoteSDP가 저장되었어요!")

if let error { PTGDataLogger.log(error.localizedDescription) }

guard self.webRTCService.peerConnection.localDescription == nil else { return }
self.webRTCService.answer { sdp in
guard let userInfo = self.remoteUserInfo else { return }
self.signalingService.send(
type: .answerSDP,
sdp: sdp,
userID: userInfo.id,
roomID: userInfo.roomID
roomID: userInfo.roomID,
offerID: userInfo.id,
answerID: sdpMessage.answerID
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이친구도 직접 webRTCService를 참조해서 set을 호출하는거보다, guard 하는부분과 set하는 부분을 명시적으로 두 함수로 분리하면 가독성이 더 좋을 것 같아요.

Comment on lines +4 to +24
enum AppPermissionManager {
static func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
print("카메라 권한 허용됨")
} else {
print("카메라 권한 거부됨")
}
}
}

static func requestMicrophonePermission() {
AVCaptureDevice.requestAccess(for: .audio) { granted in
if granted {
print("마이크 권한 허용됨")
} else {
print("마이크 권한 거부됨")
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 매니저에서 마이크 버튼 on/off 할때도 권한 여부를 확인할 필요가 있을 것 같아요.

Copy link
Collaborator

@0Hooni 0Hooni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라이브 코드리뷰 완

LGTM 👍

@Kiyoung-Kim-57 Kiyoung-Kim-57 merged commit b74d9be into feat/#79-refactor-waiting-viewmodel Nov 30, 2024
@Kiyoung-Kim-57 Kiyoung-Kim-57 deleted the feat/#130-fix-multiple-signaling branch November 30, 2024 07:06
@youn9k youn9k linked an issue Dec 1, 2024 that may be closed by this pull request
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔧 fix 버그 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

여러 사용자가 연결되지 않는 문제를 수정합니다.

5 participants