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
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ public final class ConnectionClientImpl: ConnectionClient {
self.signalingService.send(
type: .offerSDP,
sdp: sdp,
userID: myID,
roomID: remoteUserInfo.roomID
roomID: remoteUserInfo.roomID,
offerID: myID,
answerID: nil
)
}
}
Expand Down Expand Up @@ -82,26 +83,85 @@ public final class ConnectionClientImpl: ConnectionClient {
}

private func bindSignalingService() {
self.signalingService.didReceiveRemoteSdpPublisher.sink { [weak self] sdp in
guard let self else { return }

guard self.webRTCService.peerConnection.remoteDescription == nil else { return }
self.webRTCService.set(remoteSdp: sdp) { error in
// MARK: 이미 방에 있던 놈들이 받는 이벤트
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
}
Comment on lines +87 to +114
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 메서드로 빼주는 건 어떨까요?


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
)
Comment on lines +116 to 128
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하는 부분을 명시적으로 두 함수로 분리하면 가독성이 더 좋을 것 같아요.

}
}
}.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 {
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
}
Comment on lines +133 to +156
Copy link
Collaborator

Choose a reason for hiding this comment

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

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


self.webRTCService.set(remoteSdp: remoteSDP) { error in
if let error = error {
PTGDataLogger.log("Error setting remote SDP: \(error.localizedDescription)")
}
}
}.store(in: &cancellables)

self.signalingService.didReceiveCandidatePublisher.sink { [weak self] candidate in
guard let self else { return }
self.webRTCService.set(remoteCandidate: candidate) { _ in }
Expand All @@ -119,8 +179,8 @@ public final class ConnectionClientImpl: ConnectionClient {
self.signalingService.send(
type: .iceCandidate,
candidate: candidate,
userID: remoteUserInfo.id,
roomID: remoteUserInfo.roomID
roomID: remoteUserInfo.roomID,
userID: remoteUserInfo.id
)
}.store(in: &cancellables)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ public enum SdpType: String, Codable {
public struct SessionDescriptionMessage: Codable {
public let sdp: String
public let type: SdpType
public let userID: String // MARK: Offer를 보내는 사람의 ID
public let roomID: String // MARK: 참가하려는 방의 ID
public let offerID: String // MARK: Offer를 보내는 사람의 ID
public let answerID: String? // MARK: Answer를 보내는 사람의 ID

public init(from rtcSessionDescription: RTCSessionDescription, userID: String, roomID: String) {
public init(from rtcSessionDescription: RTCSessionDescription, roomID: String, offerID: String, answerID: String?) {
self.sdp = rtcSessionDescription.sdp
self.userID = userID
self.roomID = roomID
self.offerID = offerID
self.answerID = answerID

switch rtcSessionDescription.type {
case .offer: self.type = .offer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import PhotoGetherNetwork
public protocol SignalingService: WebSocketClientDelegate {
var didConnectPublisher: AnyPublisher<Void, Never> { get }
var didDidDisconnectPublisher: AnyPublisher<Void, Never> { get }
var didReceiveRemoteSdpPublisher: AnyPublisher<RTCSessionDescription, Never> { get }
var didReceiveOfferSdpPublisher: AnyPublisher<SessionDescriptionMessage, Never> { get }
var didReceiveAnswerSdpPublisher: AnyPublisher<SessionDescriptionMessage, Never> { get }
var didReceiveCandidatePublisher: AnyPublisher<RTCIceCandidate, Never> { get }

func connect()
func send(
type: SignalingRequestDTO.SignalingMessageType,
sdp rtcSdp: RTCSessionDescription,
userID: String,
roomID: String
sdp: RTCSessionDescription,
roomID: String,
offerID: String,
answerID: String?
)
func send(
type: SignalingRequestDTO.SignalingMessageType,
candidate rtcIceCandidate: RTCIceCandidate,
userID: String,
roomID: String
candidate: RTCIceCandidate,
roomID: String,
userID: String
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ final public class SignalingServiceImpl: SignalingService {

private let didConnectSubject = PassthroughSubject<Void, Never>()
private let didDisconnectSubject = PassthroughSubject<Void, Never>()
private let didReceiveRemoteSdpSubject = PassthroughSubject<RTCSessionDescription, Never>()
private let didReceiveOfferSdpSubject = PassthroughSubject<SessionDescriptionMessage, Never>()
private let didReceiveAnswerSdpSubject = PassthroughSubject<SessionDescriptionMessage, Never>()
private let didReceiveCandidateSubject = PassthroughSubject<RTCIceCandidate, Never>()

public var didConnectPublisher: AnyPublisher<Void, Never> {
Expand All @@ -19,8 +20,11 @@ final public class SignalingServiceImpl: SignalingService {
public var didDidDisconnectPublisher: AnyPublisher<Void, Never> {
self.didDisconnectSubject.eraseToAnyPublisher()
}
public var didReceiveRemoteSdpPublisher: AnyPublisher<RTCSessionDescription, Never> {
self.didReceiveRemoteSdpSubject.eraseToAnyPublisher()
public var didReceiveOfferSdpPublisher: AnyPublisher<SessionDescriptionMessage, Never> {
self.didReceiveOfferSdpSubject.eraseToAnyPublisher()
}
public var didReceiveAnswerSdpPublisher: AnyPublisher<SessionDescriptionMessage, Never> {
self.didReceiveAnswerSdpSubject.eraseToAnyPublisher()
}
public var didReceiveCandidatePublisher: AnyPublisher<RTCIceCandidate, Never> {
self.didReceiveCandidateSubject.eraseToAnyPublisher()
Expand All @@ -37,12 +41,13 @@ final public class SignalingServiceImpl: SignalingService {

public func send(
type: SignalingRequestDTO.SignalingMessageType,
sdp rtcSdp: RTCSessionDescription,
userID: String,
roomID: String
sdp: RTCSessionDescription,
roomID: String,
offerID: String,
answerID: String?
) {
PTGDataLogger.log("send SDP type: \(type) userID: \(userID) roomID: \(roomID)")
let message = SessionDescriptionMessage(from: rtcSdp, userID: userID, roomID: roomID)
PTGDataLogger.log("send SDP type: \(type) roomID: \(roomID) offerID: \(offerID) answerID: \(answerID ?? "nil")")
let message = SessionDescriptionMessage(from: sdp, roomID: roomID, offerID: offerID, answerID: answerID)
do {
let dataMessage = try self.encoder.encode(message)
let dto = SignalingRequestDTO(messageType: type, message: dataMessage)
Expand All @@ -56,11 +61,11 @@ final public class SignalingServiceImpl: SignalingService {

public func send(
type: SignalingRequestDTO.SignalingMessageType,
candidate rtcIceCandidate: RTCIceCandidate,
userID: String,
roomID: String
candidate: RTCIceCandidate,
roomID: String,
userID: String
) {
let message = IceCandidateMessage(from: rtcIceCandidate, userID: userID, roomID: roomID)
let message = IceCandidateMessage(from: candidate, userID: userID, roomID: roomID)
do {
let dataMessage = try self.encoder.encode(message)
let dto = SignalingRequestDTO(messageType: type, message: dataMessage)
Expand Down Expand Up @@ -99,12 +104,14 @@ extension SignalingServiceImpl {
case .offerSDP:
guard let sdp = response.message?.toDTO(type: SessionDescriptionMessage.self, decoder: decoder)
else { return }
self.didReceiveRemoteSdpSubject.send(sdp.rtcSessionDescription)
PTGDataLogger.log("Received Offer SDP: \(sdp)")
self.didReceiveOfferSdpSubject.send(sdp)

case .answerSDP:
guard let sdp = response.message?.toDTO(type: SessionDescriptionMessage.self, decoder: decoder)
else { return }
self.didReceiveRemoteSdpSubject.send(sdp.rtcSessionDescription)
PTGDataLogger.log("Received Answer SDP: \(sdp)")
self.didReceiveAnswerSdpSubject.send(sdp)

@unknown default:
PTGDataLogger.log("Unknown Message Type: \(response)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import PhotoGetherDomainInterface

public final class GetRemoteVideoUseCaseImpl: GetRemoteVideoUseCase {
public func execute() -> [UIView] {
connectionRepository.clients.map { $0.remoteVideoView }
let sortedClients = connectionRepository.clients
.sorted { lhs, rhs in
let lhsPosition = lhs.remoteUserInfo?.viewPosition.rawValue ?? Int.max
let rhsPosition = rhs.remoteUserInfo?.viewPosition.rawValue ?? Int.max
return lhsPosition < rhsPosition
}
.map { $0.remoteVideoView }

return sortedClients
}

private let connectionRepository: ConnectionRepository
Expand Down
6 changes: 6 additions & 0 deletions PhotoGether/PhotoGether/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
//앱이 시작될 때 카메라 권한을 요청함
AppPermissionManager.requestCameraPermission()

//앱이 시작될 때 음성 권한을 요청함
AppPermissionManager.requestMicrophonePermission()

return true
}

Expand Down
38 changes: 19 additions & 19 deletions PhotoGether/PhotoGether/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let urlString = Bundle.main.object(forInfoDictionaryKey: "BASE_URL") as? String ?? ""
let url = URL(string: urlString)!
guard let url = Secrets.BASE_URL else { return }
guard let stunServers = Secrets.STUN_SERVERS else { return }
debugPrint("SignalingServer URL: \(url)")

var isHost: Bool = true
Expand All @@ -40,33 +40,25 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
webSocketClient: webScoketClient
)

let webRTCService: WebRTCService = WebRTCServiceImpl(
iceServers: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302"
]
)

let connectionRepository: ConnectionRepository = ConnectionRepositoryImpl(
clients: [
makeConnectionClient(
signalingService: signalingService,
webRTCService: webRTCService
),
makeConnectionClient(
signalingService: signalingService,
webRTCService: webRTCService
webRTCService: makeWebRTCService(
iceServers: stunServers
)
),
makeConnectionClient(
signalingService: signalingService,
webRTCService: webRTCService
webRTCService: makeWebRTCService(
iceServers: stunServers
)
),
makeConnectionClient(
signalingService: signalingService,
webRTCService: webRTCService
webRTCService: makeWebRTCService(
iceServers: stunServers
)
)
],
roomService: roomService
Expand Down Expand Up @@ -143,6 +135,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window?.makeKeyAndVisible()
}

private func makeWebRTCService(
iceServers: [String]
) -> WebRTCService {
return WebRTCServiceImpl(
iceServers: iceServers
)
}

private func makeConnectionClient(
signalingService: SignalingService,
webRTCService: WebRTCService
Expand Down
2 changes: 2 additions & 0 deletions PhotoGether/PhotoGether/Resource/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string>$(BASE_URL)</string>
<key>EMOJI_API_KEY</key>
<string>$(EMOJI_API_KEY)</string>
<key>STUN_SERVERS</key>
<string>$(STUN_SERVERS)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand Down
24 changes: 24 additions & 0 deletions PhotoGether/PhotoGether/Source/AppPermissionManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation
import AVFoundation

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("마이크 권한 거부됨")
}
}
}
}
Comment on lines +4 to +24
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 할때도 권한 여부를 확인할 필요가 있을 것 같아요.

15 changes: 15 additions & 0 deletions PhotoGether/PhotoGether/Source/Secrets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation
// swiftlint:disable identifier_name
enum Secrets {
static var BASE_URL: URL? {
let urlString = Bundle.main.object(forInfoDictionaryKey: "BASE_URL") as? String ?? ""
return URL(string: urlString)
}

static var STUN_SERVERS: [String]? {
guard let serversString = Bundle.main.infoDictionary?["STUN_SERVERS"] as? String else {
return nil
}
return serversString.components(separatedBy: ",")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public final class WaitingRoomViewModel {

private func handleLinkButtonDidTap() {
createRoomUseCase.execute()
.receive(on: RunLoop.main)
.sink(receiveCompletion: { [weak self] completion in
if case let .failure(error) = completion {
debugPrint(error.localizedDescription)
Expand Down
Loading