Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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 switchLocalAudioTrackState(isEnable: Bool) {
self.webRTCService.changeLocalAudioState(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,11 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {
public var didEnterNewUserPublisher: AnyPublisher<(UserInfo, UIView), Never> {
didEnterNewUserSubject.eraseToAnyPublisher()
}
private let didChangeLocalAudioTrackStateSubject = CurrentValueSubject<Bool, Never>(true)
public var didChangeLocalAudioTrackStatePublisher: AnyPublisher<Bool, Never> {
didChangeLocalAudioTrackStateSubject
.eraseToAnyPublisher()
}
private let _localVideoView = CapturableVideoView()
public private(set) var localUserInfo: UserInfo?

Expand Down Expand Up @@ -73,7 +78,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 +148,14 @@ public final class ConnectionRepositoryImpl: ConnectionRepository {
)
}
}

public func switchLocalAudioTrackState() {
let presentAudioState = didChangeLocalAudioTrackStateSubject.value
clients.forEach {
$0.switchLocalAudioTrackState(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 changeLocalAudioState(_ 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
]
RTCAudioSessionConfiguration.setWebRTC(audioConfig)

let mediaConstraint = PeerConnectionSupport.mediaConstraint()
Expand All @@ -57,15 +62,16 @@ 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.peerConnection.delegate = self
Expand Down Expand Up @@ -222,12 +228,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 +275,10 @@ public extension WebRTCServiceImpl {
self.setAudioEnabled(true)
}

func changeLocalAudioState(_ 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,15 @@
import Combine
import PhotoGetherDomainInterface

public final class ChangeLocalMicStateUseCaseImpl: ChangeLocalMicStateUseCase {
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 switchLocalAudioTrackState(isEnable: Bool)
func bindLocalVideo(videoSource: RTCVideoSource?, _ localVideoView: UIView)
func bindRemoteVideo()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Combine

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

var localUserInfo: UserInfo? { get }

Expand All @@ -13,5 +14,6 @@ public protocol ConnectionRepository {
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,5 @@
import Combine

public protocol ChangeLocalMicStateUseCase {
func execute() -> AnyPublisher<Bool, Never>
}
16 changes: 11 additions & 5 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,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
connectionRepository: connectionRepository
)

let changeLocalMicStateUseCase = ChangeLocalMicStateUseCaseImpl(
connectionRepository: connectionRepository
)

let photoRoomViewModel: PhotoRoomViewModel = PhotoRoomViewModel(
captureVideosUseCase: captureVideosUseCase,
stopVideoCaptureUseCase: stopVideoCaptureUseCase,
getUserInfoUseCase: getLocalVideoUseCase
getUserInfoUseCase: getLocalVideoUseCase,
changeLocalMicStateUseCase: changeLocalMicStateUseCase
)

let localDataSource = LocalShapeDataSourceImpl()
Expand Down Expand Up @@ -196,7 +201,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
getLocalVideoUseCase: getLocalVideoUseCase,
getRemoteVideoUseCase: getRemoteVideoUseCase,
createRoomUseCase: createRoomUseCase,
didEnterNewUserPublisherUseCase: didEnterNewUserPublisherUseCase
didEnterNewUserPublisherUseCase: didEnterNewUserPublisherUseCase,
changeLocalMicStateUseCase: changeLocalMicStateUseCase
)

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 changeMicState(_ 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 @@ -14,9 +14,9 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
private let editPhotoRoomHostViewController: EditPhotoRoomHostViewController
private let editPhotoRoomGuestViewController: EditPhotoRoomGuestViewController
private let photoRoomBottomView: PhotoRoomBottomView
private let micButton: PTGMicButton
private var isHost: Bool


private let input = PassthroughSubject<PhotoRoomViewModel.Input, Never>()

private let viewModel: PhotoRoomViewModel
Expand All @@ -34,6 +34,7 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
self.viewModel = viewModel
self.isHost = isHost
self.photoRoomBottomView = PhotoRoomBottomView(isHost: isHost)
self.micButton = PTGMicButton(micState: .on)

super.init(nibName: nil, bundle: nil)
}
Expand Down Expand Up @@ -69,7 +70,7 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
}

public func addViews() {
[navigationView, participantsGridView, photoRoomBottomView].forEach {
[navigationView, participantsGridView, photoRoomBottomView, micButton].forEach {
view.addSubview($0)
}
}
Expand All @@ -92,6 +93,14 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(Constants.bottomViewHeight)
}

micButton.snp.makeConstraints {
$0.bottom.equalTo(photoRoomBottomView.snp.top)
.inset(Constants.micButtonBottomSpacing)
$0.leading.equalTo(view.safeAreaLayoutGuide)
.offset(Constants.micButtonLeadingSpacing)
$0.size.equalTo(Constants.circleButtonSize)
}
}

public func configureUI() {
Expand All @@ -108,6 +117,11 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
self?.input.send(.cameraButtonTapped)
}
.store(in: &cancellables)

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

public func bindOutput() {
Expand All @@ -132,6 +146,9 @@ public final class PhotoRoomViewController: BaseViewController, ViewControllerCo
editPhotoRoomGuestViewController.inject(frameImageGenerator, userInfo: userInfo)
self.navigationController?.pushViewController(editPhotoRoomGuestViewController, animated: true)
}
case .micMuteState(let isOn):
micButton.changeMicState(isOn)
return
}
}
.store(in: &cancellables)
Expand All @@ -142,5 +159,8 @@ extension PhotoRoomViewController {
private enum Constants {
static let bottomViewHeight: CGFloat = 80
static let navigationHeight: CGFloat = 48
static let circleButtonSize: CGSize = CGSize(width: 52, height: 52)
static let micButtonBottomSpacing: CGFloat = -4
static let micButtonLeadingSpacing: CGFloat = 16
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,32 @@ public final class PhotoRoomViewModel {

enum Input {
case cameraButtonTapped
case micButtonTapped
}

enum Output {
case timer(count: Int)
case timerCompleted(images: [UIImage], userInfo: UserInfo?)
case micMuteState(Bool)
}

private var output = PassthroughSubject<Output, Never>()
private var userInfo: UserInfo?
private let captureVideosUseCase: CaptureVideosUseCase
private let stopVideoCaptureUseCase: StopVideoCaptureUseCase
private let getUserInfoUseCase: GetLocalVideoUseCase
private let changeLocalMicStateUseCase: ChangeLocalMicStateUseCase

public init(
captureVideosUseCase: CaptureVideosUseCase,
stopVideoCaptureUseCase: StopVideoCaptureUseCase,
getUserInfoUseCase: GetLocalVideoUseCase
getUserInfoUseCase: GetLocalVideoUseCase,
changeLocalMicStateUseCase: ChangeLocalMicStateUseCase
) {
self.captureVideosUseCase = captureVideosUseCase
self.stopVideoCaptureUseCase = stopVideoCaptureUseCase
self.getUserInfoUseCase = getUserInfoUseCase
self.changeLocalMicStateUseCase = changeLocalMicStateUseCase
}

func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never> {
Expand All @@ -38,6 +43,8 @@ public final class PhotoRoomViewModel {
switch $0 {
case .cameraButtonTapped:
self.startTimer()
case .micButtonTapped:
self.handleMicButtonDidTap()
}
}.store(in: &cancellables)

Expand Down Expand Up @@ -71,4 +78,11 @@ public final class PhotoRoomViewModel {
}
}
}

private func handleMicButtonDidTap() {
changeLocalMicStateUseCase.execute()
.sink { [weak self] state in
self?.output.send(.micMuteState(state))
}.store(in: &cancellables)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ final class WaitingRoomView: UIView {
fatalError("init(coder:) has not been implemented")
}

func toggleMicButtonState() {
micButton.toggleMicState()
func changeMicButtonState(isOn: Bool) {
micButton.changeMicState(isOn)
}

func updateStartButtonTitle(count: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public final class WaitingRoomViewController: BaseViewController {
updateParticipantNickname(nickname: nickname, position: participantPosition)

// MARK: 마이크 음소거 UI 업데이트
case .micMuteState:
case .micMuteState(let isOn):
waitingRoomView.changeMicButtonState(isOn: isOn)
return

// MARK: 초대를 위한 공유시트 present
Expand Down
Loading