Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1dd8092
feat/#162 :: 오디오 입출력 옵션 추가
Kiyoung-Kim-57 Dec 2, 2024
7f48077
feat/#162 :: ConnectionClient들에게 전달하는 AudioTrack의 송출을 끊음
Kiyoung-Kim-57 Dec 2, 2024
9d59532
feat/#162 :: ConnectionRepository에서 ConnectionClients의 AudioTrack 상태 …
Kiyoung-Kim-57 Dec 2, 2024
aceb7c1
feat/#162 :: LocalMicState를 바꾸는 UseCase 생성
Kiyoung-Kim-57 Dec 2, 2024
c126b36
feat/#162 :: WaitingRoomVIewModel에서 handleMicButtonDidTap 구현
Kiyoung-Kim-57 Dec 2, 2024
c438905
chore/#162 :: changeLocalMicStateUseCase 의존성 주입
Kiyoung-Kim-57 Dec 2, 2024
fe5dcb7
feat/#162 :: PhotoRoomView에도 마이크 버튼 추가 후 UseCase 연결
Kiyoung-Kim-57 Dec 2, 2024
16d7a75
chore/#162 :: 오타 수정 및 의존성 주입
Kiyoung-Kim-57 Dec 2, 2024
2d2569c
chore/#162 :: 오디오 옵션 주석 추가
Kiyoung-Kim-57 Dec 2, 2024
728b636
chore/#162 :: 네이밍 변경
Kiyoung-Kim-57 Dec 2, 2024
e5e5e5c
feat/#162 :: 첫 시작을 마이크가 꺼진 상태로 시작하도록 변경
Kiyoung-Kim-57 Dec 2, 2024
3d6b748
feat/#162 :: 오디오 트랙 초기 설정 추가
Kiyoung-Kim-57 Dec 2, 2024
0df8bc8
feat/#162 :: micMuteState -> voiceInputState 로 변경
Kiyoung-Kim-57 Dec 3, 2024
3de7564
feat/#162 :: PhotoRoomView로 넘어갈 때 현재 마이크 상태 전달 후 마이크 버튼 뷰 업데이트
Kiyoung-Kim-57 Dec 3, 2024
4cdcb51
feat/#162 :: 편집화면 마이크 버튼 추가
0Hooni Dec 3, 2024
63736a4
Merge branch 'feat/#162-mic-mute' of github.com:boostcampwm-2024/iOS0…
0Hooni Dec 3, 2024
4e6c630
fix/#162 :: 촬영 시작시 elipsis 숨김
0Hooni Dec 3, 2024
82e9fce
feat/#162 :: EditPhotoRoom에서 마이크 버튼 input, output 연결
Kiyoung-Kim-57 Dec 3, 2024
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 @@ -76,6 +76,10 @@ public final class ConnectionClientImpl: ConnectionClient {
return capturedImage
}

public func toggleLocalAudioTrackState(isEnable: Bool) {
self.webRTCService.setLocalAudioState(isEnable)
}

/// remoteVideoTrack과 상대방의 화면을 볼 수 있는 뷰를 바인딩합니다.
public func bindRemoteVideo() {
guard let remoteVideoView = remoteVideoView as? RTCMTLVideoView else { return }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {
public var didEnterNewUserPublisher: AnyPublisher<(UserInfo, UIView), Never> {
didEnterNewUserSubject.eraseToAnyPublisher()
}
private let didChangeLocalAudioTrackStateSubject = CurrentValueSubject<Bool, Never>(false)
public var didChangeLocalAudioTrackStatePublisher: AnyPublisher<Bool, Never> {
didChangeLocalAudioTrackStateSubject
.eraseToAnyPublisher()
}
public var currentLocalVideoInputState: Bool {
didChangeLocalAudioTrackStateSubject.value
}
private let _localVideoView = CapturableVideoView()
public private(set) var localUserInfo: UserInfo?

Expand Down Expand Up @@ -73,7 +81,6 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {
let width2 = CMVideoFormatDescriptionGetDimensions(frame2.formatDescription).width
return width1 < width2
}).first else { return }

// 가장 높은 fps 선택
guard let fps = (format.videoSupportedFrameRateRanges
.sorted { return $0.maxFrameRate < $1.maxFrameRate })
Expand Down Expand Up @@ -144,6 +151,14 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {
)
}
}

public func switchLocalAudioTrackState() {
let presentAudioState = didChangeLocalAudioTrackStateSubject.value
clients.forEach {
$0.toggleLocalAudioTrackState(isEnable: !presentAudioState)
}
didChangeLocalAudioTrackStateSubject.send(!presentAudioState)
}
}

extension ConnectionRepositoryImpl {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ public protocol WebRTCService: RTCPeerConnectionDelegate, RTCDataChannelDelegate
// MARK: Audio
func muteAudio()
func unmuteAudio()
func setLocalAudioState(_ isEnabled: Bool)
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public final class WebRTCServiceImpl: NSObject, WebRTCService {
]
private var localVideoTrack: RTCVideoTrack?
private var remoteVideoTrack: RTCVideoTrack?
private var localAudioTrack: RTCAudioTrack
private var localDataChannel: RTCDataChannel?
private var remoteDataChannel: RTCDataChannel?

Expand All @@ -42,7 +43,11 @@ public final class WebRTCServiceImpl: NSObject, WebRTCService {
let audioConfig = RTCAudioSessionConfiguration.webRTC()
audioConfig.category = AVAudioSession.Category.playAndRecord.rawValue
audioConfig.mode = AVAudioSession.Mode.voiceChat.rawValue
audioConfig.categoryOptions = [.defaultToSpeaker]
audioConfig.categoryOptions = [
.defaultToSpeaker, // 하단 스피커를 기본으로 설정
.allowBluetooth, // 블루투스 기기의 음성 입출력 지원
.allowAirPlay // AirPlay를 통해 연결된 다른 기기로 음성 출력 지원
]
RTCAudioSessionConfiguration.setWebRTC(audioConfig)

let mediaConstraint = PeerConnectionSupport.mediaConstraint()
Expand All @@ -57,16 +62,18 @@ public final class WebRTCServiceImpl: NSObject, WebRTCService {
}

self.peerConnection = peerConnection
// MARK: AudioTrack 생성
self.localAudioTrack = PeerConnectionSupport.createAudioTrack()

super.init()

// MARK: DataChannel 연결
self.connectDataChannel(dataChannel: createDataChannel())

// MARK: AudioTrack 연결
let audioTrack = PeerConnectionSupport.createAudioTrack()
self.connectAudioTrack(audioTrack: audioTrack)
self.connectAudioTrack(audioTrack: self.localAudioTrack)
self.configureAudioSession()
self.localAudioTrack.isEnabled = false

self.peerConnection.delegate = self
self.bindNoti()
Expand Down Expand Up @@ -222,12 +229,12 @@ public extension WebRTCServiceImpl {
private func configureAudioSession() {
self.rtcAudioSession.lockForConfiguration()
do {

try self.rtcAudioSession.setCategory(
.playAndRecord,
mode: .voiceChat,
options: .defaultToSpeaker
)
try self.rtcAudioSession.overrideOutputAudioPort(.speaker)
try self.rtcAudioSession.setActive(true)
} catch let error {
PTGLogger.default.log("Error changeing AVAudioSession category: \(error)")
Expand Down Expand Up @@ -269,6 +276,10 @@ public extension WebRTCServiceImpl {
self.setAudioEnabled(true)
}

func setLocalAudioState(_ isEnabled: Bool) {
self.localAudioTrack.isEnabled = isEnabled
}

private func setAudioEnabled(_ isEnabled: Bool) {
setTrackEnabled(RTCAudioTrack.self, isEnabled: isEnabled)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PhotoGetherDomainInterface

public final class GetVoiceInputStateUseCaseImpl: GetVoiceInputStateUseCase {
public func execute() -> Bool {
return connectionRepository.currentLocalVideoInputState
}

private let connectionRepository: ConnectionRepository

public init(connectionRepository: ConnectionRepository) {
self.connectionRepository = connectionRepository
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Combine
import PhotoGetherDomainInterface

public final class ToggleLocalMicStateUseCaseImpl: ToggleLocalMicStateUseCase {
public func execute() -> AnyPublisher<Bool, Never> {
connectionRepository.switchLocalAudioTrackState()
return connectionRepository.didChangeLocalAudioTrackStatePublisher
}

private let connectionRepository: ConnectionRepository

public init(connectionRepository: ConnectionRepository) {
self.connectionRepository = connectionRepository
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public protocol ConnectionClient {
func setRemoteUserInfo(_ remoteUserInfo: UserInfo)
func sendData(data: Data)
func captureVideo() -> UIImage
func toggleLocalAudioTrackState(isEnable: Bool)
func bindLocalVideo(videoSource: RTCVideoSource?, _ localVideoView: UIView)
func bindRemoteVideo()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import Combine

public protocol ConnectionRepository {
var didEnterNewUserPublisher: AnyPublisher<(UserInfo, UIView), Never> { get }
var didChangeLocalAudioTrackStatePublisher: AnyPublisher<Bool, Never> { get }

var localUserInfo: UserInfo? { get }

var clients: [ConnectionClient] { get }
var localVideoView: UIView { get }
var capturedLocalVideo: UIImage? { get }
var currentLocalVideoInputState: Bool { get }

func createRoom() -> AnyPublisher<RoomOwnerEntity, Error>
func joinRoom(to roomID: String, hostID: String) -> AnyPublisher<Bool, Error>
func sendOffer() async throws
func stopCaptureLocalVideo() -> Bool
func stopCaptureLocalVideo() -> Bool
func switchLocalAudioTrackState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol GetVoiceInputStateUseCase {
func execute() -> Bool
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Combine

public protocol ToggleLocalMicStateUseCase {
func execute() -> AnyPublisher<Bool, Never>
}
29 changes: 22 additions & 7 deletions PhotoGether/PhotoGether/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
roomOwnerEntity = DeepLinkParser.parseRoomInfo(from: urlContext.url)
}

let webScoketClient: WebSocketClient = WebSocketClientImpl(url: url)
let webSocketClient: WebSocketClient = WebSocketClientImpl(url: url)

let roomService: RoomService = RoomServiceImpl(
webSocketClient: webScoketClient
webSocketClient: webSocketClient
)

let signalingService: SignalingService = SignalingServiceImpl(
webSocketClient: webScoketClient
webSocketClient: webSocketClient
)

let connectionRepository: ConnectionRepository = ConnectionRepositoryImpl(
Expand Down Expand Up @@ -90,10 +90,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
connectionRepository: connectionRepository
)

let toggleLocalMicStateUseCaseImpl = ToggleLocalMicStateUseCaseImpl(
connectionRepository: connectionRepository
)

let getVoiceInputStateUseCaseImpl = GetVoiceInputStateUseCaseImpl(
connectionRepository: connectionRepository
)

let photoRoomViewModel: PhotoRoomViewModel = PhotoRoomViewModel(
captureVideosUseCase: captureVideosUseCase,
stopVideoCaptureUseCase: stopVideoCaptureUseCase,
getUserInfoUseCase: getLocalVideoUseCase
getUserInfoUseCase: getLocalVideoUseCase,
toggleLocalMicStateUseCase: toggleLocalMicStateUseCaseImpl,
getVoiceInputStateUseCase: getVoiceInputStateUseCaseImpl
)

let localDataSource = LocalShapeDataSourceImpl()
Expand Down Expand Up @@ -150,7 +160,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
receiveStickerListUseCase: receiveStickerListHostUseCase,
receiveFrameUseCase: receiveFrameHostUseCase,
sendStickerToRepositoryUseCase: sendStickerToRepositoryHostUseCase,
sendFrameToRepositoryUseCase: sendFrameToRepositoryHostUseCase
sendFrameToRepositoryUseCase: sendFrameToRepositoryHostUseCase,
toggleLocalMicStateUseCase: toggleLocalMicStateUseCaseImpl,
getVoiceInputStateUseCase: getVoiceInputStateUseCaseImpl
)

let stickerBottomSheetViewModel = StickerBottomSheetViewModel(
Expand All @@ -174,7 +186,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
receiveStickerListUseCase: receiveStickerListGuestUseCase,
receiveFrameUseCase: receiveFrameGuestUseCase,
sendStickerToRepositoryUseCase: sendStickerToRepositoryGuestUseCase,
sendFrameToRepositoryUseCase: sendFrameToRepositoryGuestUseCase
sendFrameToRepositoryUseCase: sendFrameToRepositoryGuestUseCase,
toggleLocalMicStateUseCase: toggleLocalMicStateUseCaseImpl,
getVoiceInputStateUseCase: getVoiceInputStateUseCaseImpl
)

let editPhotoRoomGuestViewController = EditPhotoRoomGuestViewController(
Expand All @@ -196,7 +210,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
getLocalVideoUseCase: getLocalVideoUseCase,
getRemoteVideoUseCase: getRemoteVideoUseCase,
createRoomUseCase: createRoomUseCase,
didEnterNewUserPublisherUseCase: didEnterNewUserPublisherUseCase
didEnterNewUserPublisherUseCase: didEnterNewUserPublisherUseCase,
toggleLocalMicStateUseCase: toggleLocalMicStateUseCaseImpl
)

let waitingRoomViewController: WaitingRoomViewController = WaitingRoomViewController(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,8 @@ public final class PTGMicButton: UIButton {
layer.cornerRadius = bounds.width / 2
}

public func toggleMicState() {
switch micState {
case .on:
micState = .off
case .off:
micState = .on
}
public func toggleMicState(_ isOn: Bool) {
micState = isOn ? .on : .off

buttonImage.image = UIImage(systemName: micState.image)
buttonImage.tintColor = micState.color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SharePhotoFeature
public class EditPhotoRoomGuestViewController: BaseViewController, ViewControllerConfigure {
private let navigationView = UIView()
private let canvasScrollView = CanvasScrollView()
private let micButton = PTGMicButton(micState: .on)
private let bottomView = EditPhotoGuestBottomView()
private let bottomSheetViewController: StickerBottomSheetViewController

Expand Down Expand Up @@ -43,6 +44,9 @@ public class EditPhotoRoomGuestViewController: BaseViewController, ViewControlle
bindInput()
bindOutput()
bindNoti()
micButton.toggleMicState(
viewModel.fetchLocalVoiceInputState()
)
input.send(.initialState)
}

Expand All @@ -55,7 +59,7 @@ public class EditPhotoRoomGuestViewController: BaseViewController, ViewControlle
}

public func addViews() {
[navigationView, canvasScrollView, bottomView].forEach {
[navigationView, canvasScrollView, bottomView, micButton].forEach {
view.addSubview($0)
}
}
Expand All @@ -78,6 +82,12 @@ public class EditPhotoRoomGuestViewController: BaseViewController, ViewControlle
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(80)
}

micButton.snp.makeConstraints {
$0.bottom.equalTo(bottomView.snp.top).offset(-4)
$0.leading.equalToSuperview().offset(16)
$0.size.equalTo(52)
}
}

public func configureUI() {
Expand All @@ -100,6 +110,12 @@ public class EditPhotoRoomGuestViewController: BaseViewController, ViewControlle
self?.input.send(.frameButtonDidTap)
}
.store(in: &cancellables)

micButton.tapPublisher
.sink { [weak self] in
self?.input.send(.micButtonDidTap)
}
.store(in: &cancellables)
}

public func bindOutput() {
Expand All @@ -115,6 +131,8 @@ public class EditPhotoRoomGuestViewController: BaseViewController, ViewControlle
self?.updateFrameImage(to: image)
case .presentStickerBottomSheet:
self?.presentStickerBottomSheet()
case .voiceInputState(let isOn):
self?.micButton.toggleMicState(isOn)
}
}
.store(in: &cancellables)
Expand Down
Loading