-
Notifications
You must be signed in to change notification settings - Fork 2
[HOTFIX/#145] 다인 연결이 안되는 문제를 해결합니다. #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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: Youngkyu Song <ace_lephant@naver.com>
Co-Authored-By: Youngkyu Song <ace_lephant@naver.com>
hsw1920
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Video 관련쪽이 코드와 네이밍만 보고는 어떤 역할을 하고 있는지 팔로업이 쉽지 않은데 라이브로 설명 부탁드립니다!
| config.sdpSemantics = .unifiedPlan | ||
| config.continualGatheringPolicy = .gatherContinually |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 이게 어떤 설정값인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 부분을 몰라서 저도 방금 찾아봤습니다!
- SdpSemantics의 경우 WebRTC가 어떻게 SDP를 처리할지에 대한 정책을 결정하는 부분이라고 합니다! UnifiedPlan이 WebRTC 표준이고 다중 미디어 트랙을 지원해서 여러 트랙을 세부적으로 관리하기 용이하다고 합니다. 그 외에는 Plan B라는 방식이 있는데 이 경우 다중 트랙처리가 제한적이고 초기 WebRTC 연결에서 쓰였던 것이라 현재는 거의 안쓰인다고 합니다.
- 두번째는 ICE Candidate 정보 수집에 대한 정책입니다! gatherContinually 옵션은 연결 수립 이후에도 계속해서 ICE Candidate를 수집하는 정책입니다. 해당 방식의 장점은 네트워크 상태가 변해도 적절한 Candidate를 계속해서 찾기 때문에 갑작스런 네트워크 품질 저하에 대응하기 좋습니다!
| let audioTrack = peerConnectionFactory.audioTrack( | ||
| with: audioSource, | ||
| trackId: "audio0" | ||
| ) | ||
| return audioTrack | ||
| } | ||
|
|
||
| static func createVideoTrack(videoSource: RTCVideoSource) -> RTCVideoTrack { | ||
| let videoTrack = peerConnectionFactory.videoTrack( | ||
| with: videoSource, | ||
| trackId: "video0" | ||
| ) | ||
| return videoTrack | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trackId가 정적으로 쓰이는데, 어떻게 사용되나요? 스태틱에서 사용하게 된다면 id가 모두가 동일할 것 같은데, 이니셜라이즈한 후에 변경이 되는지 궁금합니다.
| // makeConnectionClient( | ||
| // webRTCService: makeWebRTCService( | ||
| // iceServers: stunServers | ||
| // ) | ||
| // ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용안하는 코드발견입니다
| //let guestVideoView2 = remoteVideoViews[safe: 1] ?? UIView() | ||
| //let guestVideoView3 = remoteVideoViews[safe: 2] ?? UIView() | ||
|
|
||
| updateParticipantView(view: guestVideoView1, position: .bottomTrailing) | ||
| updateParticipantView(view: guestVideoView2, position: .topTrailing) | ||
| updateParticipantView(view: guestVideoView3, position: .bottomLeading) | ||
| //updateParticipantView(view: guestVideoView2, position: .topTrailing) | ||
| //updateParticipantView(view: guestVideoView3, position: .bottomLeading) | ||
|
|
||
| updateParticipantNickname(nickname: "나는 게스트1", position: .bottomTrailing) | ||
| updateParticipantNickname(nickname: "나는 게스트2", position: .topTrailing) | ||
| updateParticipantNickname(nickname: "나는 게스트3", position: .bottomLeading) | ||
| //updateParticipantNickname(nickname: "나는 게스트2", position: .topTrailing) | ||
| //updateParticipantNickname(nickname: "나는 게스트3", position: .bottomLeading) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디버깅용 임시코드인가요?
0Hooni
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
드디어 문제 해결 완료!!
수고 많으셨습니다 👍
| /// 좌우반전 | ||
| func flipHorizontally() -> Self { | ||
| (self as! UIView).transform = CGAffineTransform(scaleX: -1.0, y: 1.0) | ||
| return self | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
드디어...!!
| return RTCMediaConstraints( | ||
| mandatoryConstraints: nil, | ||
| optionalConstraints: [ | ||
| "DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
키 네임이 뭘 의미하는건가요 🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DTLS (Datagram Transport Layer Security) 와 SRTP (Secure Real-time Transport Protocol) 를 사용하는 옵션인데 자세한 내용을 찾아보진 못했지만 미디어 스트림을 암호화하여 안전하게 통신할 수 있는 옵션입니다!
🤔 배경
이제 진짜 되게 해야한다.. 는 일념으로 트러블 슈팅했습니다.
📃 작업 내역
무수한 로그들과 여러 실험을 거친 결과 예상되는 문제를 찾았습니다.
ConnectionRepostiory를 init 할 때, ConnectionClient의 배열을 넣어주는데 해당 배열안에 ConnectionClient가 1개만 존재하면 100%확률로 연결에 성공하고, 2개 이상이면 반드시 실패한다는 것을 알게 되었습니다.
localVideo 화면은 잘 보이는 반면, 상대에게 내 화면이 간헐적으로만 1프레임씩 전달된다는 부분을 의심했고 내 화면을 상대방에게 어떻게 전달하고 있는지 코드를 통해 플로우를 확인해보았습니다.
Important
bindLocalVideo()함수를 호출bindLocalVideo()에서 ConnectionClient의 배열을 순회하며ConnectionClient.bindLocalVideo(:View)를 호출ConnectionClient.bindLocalVideo(:View)에서webRTCService.startCaptureLocalVideo(:View)를 호출 << 문제webRTCService.startCaptureLocalVideo(:View)에서 시스템 카메라를 통해 실시간 캡쳐 시작위 플로우 중
webRTCService.startCaptureLocalVideo(:View)는 시스템 카메라에 접근하는데, 2번 플로우에 의해 ConnectionClient의 배열의 요소만큼 호출하게 되고 시스템 카메라 라는 공유 자원에 반복 접근하고 있어 문제가 되는게 아닐까? 🤔 라는 생각이 들었습니다.결론적으로 해당 문제가 맞았고, 아래와 같은 플로우로 변경해 문제를 해결했습니다.
Important
initVideoSource()함수를 호출initVideoCapturer()함수를 호출startCaptureLocalVideo()함수를 호출이를 통해 내 카메라 화면의 VideoSource를 ConnectionRepository에서 하나만 들고 있게 됨
bindLocalVideo()함수를 호출bindLocalVideo()에서 ConnectionClient의 배열을 순회하며ConnectionClient.bindLocalVideo(:videoSource, :view)호출ConnectionClient.bindLocalVideo(:videoSource, :view)에서webRTCService.connectLocalVideoTrack(:videoTrack),webRTCService.renderLocalVideo(:view)순서대로 호출webRTCService.connectLocalVideoTrack(:videoTrack)에서 localVideoTrack과 PeerConnection에 addwebRTCService.renderLocalVideo(:view)에서 localVideoTrack에 뷰를 add (localVideoTrack에 담기는 화면을 뷰로 렌더링)👀 한줄 요약: 시스템 카메라에 여러 군데에서 동시 접근하고 있었고, ConnectionRepository에서 한번만 접근하도록 변경
✅ 리뷰 노트
흐름이 정말 여기갔다 저기갔다해서 파악이 어렵습니다.. 죄송합니다 흑흑
🎨 스크린샷
🚀 테스트 방법
앱 타겟으로 실행해 최대 4명 연결해볼 수 있습니다!