Merge branch 'charlesmchen/webrtc/video4' into feature/webrtc

pull/1/head
Matthew Chen 8 years ago
commit 2a4170a32d

@ -42,18 +42,25 @@ import Foundation
internal func speakerphoneDidChange(call: SignalCall, isEnabled: Bool) { internal func speakerphoneDidChange(call: SignalCall, isEnabled: Bool) {
AssertIsOnMainThread() AssertIsOnMainThread()
if isEnabled {
ensureIsEnabled(call: call)
}
internal func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool) {
AssertIsOnMainThread()
ensureIsEnabled(call: call)
}
private func ensureIsEnabled(call: SignalCall) {
// Auto-enable speakerphone when local video is enabled.
if call.isSpeakerphoneEnabled || call.hasLocalVideo {
setAudioSession(category: AVAudioSessionCategoryPlayAndRecord, options: .defaultToSpeaker) setAudioSession(category: AVAudioSessionCategoryPlayAndRecord, options: .defaultToSpeaker)
} else { } else {
setAudioSession(category: AVAudioSessionCategoryPlayAndRecord) setAudioSession(category: AVAudioSessionCategoryPlayAndRecord)
} }
} }
internal func hasVideoDidChange(call: SignalCall, hasVideo: Bool) {
AssertIsOnMainThread()
// no-op
}
// MARK: - Service action handlers // MARK: - Service action handlers
public func handleState(_ state: CallState) { public func handleState(_ state: CallState) {

@ -770,7 +770,7 @@ protocol CallServiceObserver: class {
* *
* Can be used for Incoming and Outgoing calls. * Can be used for Incoming and Outgoing calls.
*/ */
func setHasVideo(hasVideo: Bool) { func setHasLocalVideo(hasLocalVideo: Bool) {
assertOnSignalingQueue() assertOnSignalingQueue()
guard let peerConnectionClient = self.peerConnectionClient else { guard let peerConnectionClient = self.peerConnectionClient else {
@ -783,13 +783,13 @@ protocol CallServiceObserver: class {
return return
} }
call.hasVideo = hasVideo call.hasLocalVideo = hasLocalVideo
peerConnectionClient.setLocalVideoEnabled(enabled: shouldHaveLocalVideoTrack()) peerConnectionClient.setLocalVideoEnabled(enabled: shouldHaveLocalVideoTrack())
} }
func handleCallKitStartVideo() { func handleCallKitStartVideo() {
CallService.signalingQueue.async { CallService.signalingQueue.async {
self.setHasVideo(hasVideo:true) self.setHasLocalVideo(hasLocalVideo:true)
} }
} }
@ -1000,9 +1000,9 @@ protocol CallServiceObserver: class {
self.updateIsVideoEnabled() self.updateIsVideoEnabled()
} }
internal func hasVideoDidChange(call: SignalCall, hasVideo: Bool) { internal func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool) {
AssertIsOnMainThread() AssertIsOnMainThread()
Logger.info("\(self.TAG) \(#function): \(hasVideo)") Logger.info("\(self.TAG) \(#function): \(hasLocalVideo)")
self.updateIsVideoEnabled() self.updateIsVideoEnabled()
} }
@ -1027,7 +1027,7 @@ protocol CallServiceObserver: class {
return (!Platform.isSimulator && return (!Platform.isSimulator &&
call != nil && call != nil &&
call!.state == .connected && call!.state == .connected &&
call!.hasVideo) call!.hasLocalVideo)
} }
private func updateIsVideoEnabled() { private func updateIsVideoEnabled() {

@ -146,14 +146,14 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee {
} }
} }
func setHasVideo(call: SignalCall, hasVideo: Bool) { func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
CallService.signalingQueue.async { CallService.signalingQueue.async {
guard call.localId == self.callService.call?.localId else { guard call.localId == self.callService.call?.localId else {
assertionFailure("\(self.TAG) in \(#function) localId does not match current call") assertionFailure("\(self.TAG) in \(#function) localId does not match current call")
return return
} }
self.callService.setHasVideo(hasVideo: hasVideo) self.callService.setHasLocalVideo(hasLocalVideo: hasLocalVideo)
} }
} }
} }

@ -20,7 +20,7 @@ enum CallState: String {
// All Observer methods will be invoked from the main thread. // All Observer methods will be invoked from the main thread.
protocol CallObserver: class { protocol CallObserver: class {
func stateDidChange(call: SignalCall, state: CallState) func stateDidChange(call: SignalCall, state: CallState)
func hasVideoDidChange(call: SignalCall, hasVideo: Bool) func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool)
func muteDidChange(call: SignalCall, isMuted: Bool) func muteDidChange(call: SignalCall, isMuted: Bool)
func speakerphoneDidChange(call: SignalCall, isEnabled: Bool) func speakerphoneDidChange(call: SignalCall, isEnabled: Bool)
} }
@ -43,20 +43,20 @@ protocol CallObserver: class {
// Distinguishes between calls locally, e.g. in CallKit // Distinguishes between calls locally, e.g. in CallKit
let localId: UUID let localId: UUID
var hasVideo = false { var hasLocalVideo = false {
didSet { didSet {
// This should only occur on the signaling queue. // This should only occur on the signaling queue.
objc_sync_enter(self) objc_sync_enter(self)
let observers = self.observers let observers = self.observers
let call = self let call = self
let hasVideo = self.hasVideo let hasLocalVideo = self.hasLocalVideo
objc_sync_exit(self) objc_sync_exit(self)
DispatchQueue.main.async { DispatchQueue.main.async {
for observer in observers { for observer in observers {
observer.value?.hasVideoDidChange(call: call, hasVideo: hasVideo) observer.value?.hasLocalVideoDidChange(call: call, hasLocalVideo: hasLocalVideo)
} }
} }
} }

@ -25,7 +25,7 @@ final class CallKitCallManager: NSObject {
let handle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber) let handle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
let startCallAction = CXStartCallAction(call: call.localId, handle: handle) let startCallAction = CXStartCallAction(call: call.localId, handle: handle)
startCallAction.isVideo = call.hasVideo startCallAction.isVideo = call.hasLocalVideo
let transaction = CXTransaction() let transaction = CXTransaction()
transaction.addAction(startCallAction) transaction.addAction(startCallAction)

@ -34,7 +34,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
providerConfiguration.supportsVideo = true providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallGroups = 1 providerConfiguration.maximumCallGroups = 1
providerConfiguration.maximumCallsPerCallGroup = 1 providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.phoneNumber] providerConfiguration.supportedHandleTypes = [.phoneNumber]
@ -82,12 +82,12 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
// Construct a CXCallUpdate describing the incoming call, including the caller. // Construct a CXCallUpdate describing the incoming call, including the caller.
let update = CXCallUpdate() let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber) update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
update.hasVideo = call.hasVideo update.hasVideo = call.hasLocalVideo
update.supportsHolding = false update.supportsHolding = false
update.supportsGrouping = false update.supportsGrouping = false
update.supportsUngrouping = false update.supportsUngrouping = false
update.supportsDTMF = false update.supportsDTMF = false
// Report the incoming call to the system // Report the incoming call to the system
provider.reportNewIncomingCall(with: call.localId, update: update) { error in provider.reportNewIncomingCall(with: call.localId, update: update) { error in
/* /*
@ -136,16 +136,16 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
callManager.setIsMuted(call: call, isMuted: isMuted) callManager.setIsMuted(call: call, isMuted: isMuted)
} }
func setHasVideo(call: SignalCall, hasVideo: Bool) { func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
let update = CXCallUpdate() let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber) update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber)
update.hasVideo = hasVideo update.hasVideo = hasLocalVideo
// Update the CallKit UI. // Update the CallKit UI.
provider.reportCall(with: call.localId, updated: update) provider.reportCall(with: call.localId, updated: update)
CallService.signalingQueue.async { CallService.signalingQueue.async {
self.callService.setHasVideo(hasVideo: hasVideo) self.callService.setHasLocalVideo(hasLocalVideo: hasLocalVideo)
} }
} }

@ -23,7 +23,7 @@ protocol CallUIAdaptee {
func remoteDidHangupCall(_ call: SignalCall) func remoteDidHangupCall(_ call: SignalCall)
func failCall(_ call: SignalCall, error: CallError) func failCall(_ call: SignalCall, error: CallError)
func setIsMuted(call: SignalCall, isMuted: Bool) func setIsMuted(call: SignalCall, isMuted: Bool)
func setHasVideo(call: SignalCall, hasVideo: Bool) func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool)
func callBack(recipientId: String) func callBack(recipientId: String)
} }
@ -145,15 +145,17 @@ extension CallUIAdaptee {
adaptee.setIsMuted(call: call, isMuted: isMuted) adaptee.setIsMuted(call: call, isMuted: isMuted)
} }
internal func setHasVideo(call: SignalCall, hasVideo: Bool) { internal func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) {
adaptee.setHasVideo(call: call, hasVideo: hasVideo) adaptee.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo)
} }
internal func toggleSpeakerphone(call: SignalCall, isEnabled: Bool) { internal func setIsSpeakerphoneEnabled(call: SignalCall, isEnabled: Bool) {
// Speakerphone is not handled by CallKit (e.g. there is no CXAction), so we handle it w/o going through the // Speakerphone is not handled by CallKit (e.g. there is no CXAction), so we handle it w/o going through the
// adaptee, relying on the AudioService CallObserver to put the system in a state consistent with the call's // adaptee, relying on the AudioService CallObserver to put the system in a state consistent with the call's
// assigned property. // assigned property.
call.isSpeakerphoneEnabled = isEnabled CallService.signalingQueue.async {
call.isSpeakerphoneEnabled = isEnabled
}
} }
// CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually. // CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually.

@ -46,10 +46,15 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
var ongoingCallView: UIView! var ongoingCallView: UIView!
var hangUpButton: UIButton! var hangUpButton: UIButton!
var muteButton: UIButton!
var speakerPhoneButton: UIButton! var speakerPhoneButton: UIButton!
var textMessageButton: UIButton! var audioModeMuteButton: UIButton!
var videoButton: UIButton! var audioModeVideoButton: UIButton!
var videoModeMuteButton: UIButton!
var videoModeVideoButton: UIButton!
// TODO: Later, we'll re-enable the text message button
// so users can send and read messages during a
// call.
// var textMessageButton: UIButton!
// MARK: Incoming Call Controls // MARK: Incoming Call Controls
@ -68,20 +73,6 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
var remoteVideoConstraints: [NSLayoutConstraint] = [] var remoteVideoConstraints: [NSLayoutConstraint] = []
var localVideoConstraints: [NSLayoutConstraint] = [] var localVideoConstraints: [NSLayoutConstraint] = []
// MARK: Control Groups
var allControls: [UIView] {
return incomingCallControls + ongoingCallControls
}
var incomingCallControls: [UIView] {
return [ acceptIncomingButton, declineIncomingButton ]
}
var ongoingCallControls: [UIView] {
return [ muteButton, speakerPhoneButton, textMessageButton, hangUpButton, videoButton ]
}
// MARK: Initializers // MARK: Initializers
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
@ -195,29 +186,38 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
func createOngoingCallControls() { func createOngoingCallControls() {
textMessageButton = createButton(imageName:"message-active-wide", // textMessageButton = createButton(imageName:"message-active-wide",
action:#selector(didPressTextMessage)) // action:#selector(didPressTextMessage))
muteButton = createButton(imageName:"mute-unselected-wide", speakerPhoneButton = createButton(imageName:"speaker-inactive-wide",
action:#selector(didPressMute))
speakerPhoneButton = createButton(imageName:"speaker-active-wide",
action:#selector(didPressSpeakerphone)) action:#selector(didPressSpeakerphone))
videoButton = createButton(imageName:"video-inactive-wide",
action:#selector(didPressVideo))
hangUpButton = createButton(imageName:"hangup-active-wide", hangUpButton = createButton(imageName:"hangup-active-wide",
action:#selector(didPressHangup)) action:#selector(didPressHangup))
audioModeMuteButton = createButton(imageName:"mute-unselected-wide",
action:#selector(didPressMute))
videoModeMuteButton = createButton(imageName:"mute-unselected-wide",
action:#selector(didPressMute))
audioModeVideoButton = createButton(imageName:"video-inactive-wide",
action:#selector(didPressVideo))
videoModeVideoButton = createButton(imageName:"video-inactive-wide",
action:#selector(didPressVideo))
let muteSelectedImage = UIImage(named:"mute-selected-wide") let muteSelectedImage = UIImage(named:"mute-selected-wide")
assert(muteSelectedImage != nil) assert(muteSelectedImage != nil)
muteButton.setImage(muteSelectedImage, for:.selected) audioModeMuteButton.setImage(muteSelectedImage, for:.selected)
videoModeMuteButton.setImage(muteSelectedImage, for:.selected)
let videoSelectedImage = UIImage(named:"video-active-wide") let videoSelectedImage = UIImage(named:"video-active-wide")
assert(videoSelectedImage != nil) assert(videoSelectedImage != nil)
videoButton.setImage(videoSelectedImage, for:.selected) audioModeVideoButton.setImage(videoSelectedImage, for:.selected)
videoModeVideoButton.setImage(videoSelectedImage, for:.selected)
let speakerPhoneSelectedImage = UIImage(named:"speaker-active-wide")
assert(speakerPhoneSelectedImage != nil)
speakerPhoneButton.setImage(speakerPhoneSelectedImage, for:.selected)
ongoingCallView = createContainerForCallControls(controlGroups : [ ongoingCallView = createContainerForCallControls(controlGroups : [
[textMessageButton, videoButton], [audioModeMuteButton, speakerPhoneButton, audioModeVideoButton ],
[muteButton, speakerPhoneButton ], [videoModeMuteButton, hangUpButton, videoModeVideoButton ]
[hangUpButton ]
]) ])
} }
@ -244,7 +244,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
var prevRow: UIView? var prevRow: UIView?
for row in rows { for row in rows {
containerView.addSubview(row) containerView.addSubview(row)
row.autoPinWidthToSuperview() row.autoHCenterInSuperview()
if prevRow != nil { if prevRow != nil {
row.autoPinEdge(.top, to:.bottom, of:prevRow!, withOffset:rowspacing) row.autoPinEdge(.top, to:.bottom, of:prevRow!, withOffset:rowspacing)
} }
@ -310,7 +310,8 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
// If there's only one subview in this row, center it. // If there's only one subview in this row, center it.
let subview = subviews.first! let subview = subviews.first!
row.addSubview(subview) row.addSubview(subview)
subview.autoCenterInSuperview() subview.autoVCenterInSuperview()
subview.autoPinWidthToSuperview()
} }
return row return row
@ -421,6 +422,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
} }
self.remoteVideoConstraints = constraints self.remoteVideoConstraints = constraints
updateCallUI(callState: call.state)
} }
internal func updateLocalVideoLayout() { internal func updateLocalVideoLayout() {
@ -442,14 +444,6 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
self.localVideoConstraints = constraints self.localVideoConstraints = constraints
} }
func traverseViewHierarchy(view: UIView!, visitor: (UIView) -> Void) {
visitor(view)
for subview in view.subviews {
traverseViewHierarchy(view:subview, visitor:visitor)
}
}
// MARK: - Methods // MARK: - Methods
// objc accessible way to set our swift enum. // objc accessible way to set our swift enum.
@ -522,8 +516,11 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
assert(Thread.isMainThread) assert(Thread.isMainThread)
updateCallStatusLabel(callState: callState) updateCallStatusLabel(callState: callState)
videoButton.isSelected = call.hasVideo audioModeMuteButton.isSelected = call.isMuted
muteButton.isSelected = call.isMuted videoModeMuteButton.isSelected = call.isMuted
audioModeVideoButton.isSelected = call.hasLocalVideo
videoModeVideoButton.isSelected = call.hasLocalVideo
speakerPhoneButton.isSelected = call.isSpeakerphoneEnabled
// Show Incoming vs. Ongoing call controls // Show Incoming vs. Ongoing call controls
let isRinging = callState == .localRinging let isRinging = callState == .localRinging
@ -532,6 +529,14 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
ongoingCallView.isHidden = isRinging ongoingCallView.isHidden = isRinging
ongoingCallView.isUserInteractionEnabled = !isRinging ongoingCallView.isUserInteractionEnabled = !isRinging
// Rework control state if remote video is available.
contactAvatarView.isHidden = !remoteVideoView.isHidden
speakerPhoneButton.isHidden = !remoteVideoView.isHidden
audioModeMuteButton.isHidden = !remoteVideoView.isHidden
videoModeMuteButton.isHidden = remoteVideoView.isHidden
audioModeVideoButton.isHidden = !remoteVideoView.isHidden
videoModeVideoButton.isHidden = remoteVideoView.isHidden
// Dismiss Handling // Dismiss Handling
switch callState { switch callState {
case .remoteHangup, .remoteBusy, .localFailure: case .remoteHangup, .remoteBusy, .localFailure:
@ -595,7 +600,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
Logger.info("\(TAG) called \(#function)") Logger.info("\(TAG) called \(#function)")
speakerphoneButton.isSelected = !speakerphoneButton.isSelected speakerphoneButton.isSelected = !speakerphoneButton.isSelected
if let call = self.call { if let call = self.call {
callUIAdapter.toggleSpeakerphone(call: call, isEnabled: speakerphoneButton.isSelected) callUIAdapter.setIsSpeakerphoneEnabled(call: call, isEnabled: speakerphoneButton.isSelected)
} else { } else {
Logger.warn("\(TAG) pressed mute, but call was unexpectedly nil") Logger.warn("\(TAG) pressed mute, but call was unexpectedly nil")
} }
@ -624,9 +629,11 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
func didPressVideo(sender: UIButton) { func didPressVideo(sender: UIButton) {
Logger.info("\(TAG) called \(#function)") Logger.info("\(TAG) called \(#function)")
videoButton.isSelected = !videoButton.isSelected let hasLocalVideo = !sender.isSelected
audioModeVideoButton.isSelected = hasLocalVideo
videoModeVideoButton.isSelected = hasLocalVideo
if let call = self.call { if let call = self.call {
callUIAdapter.setHasVideo(call: call, hasVideo: videoButton.isSelected) callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo)
} else { } else {
Logger.warn("\(TAG) pressed video, but call was unexpectedly nil") Logger.warn("\(TAG) pressed video, but call was unexpectedly nil")
} }
@ -655,7 +662,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
self.updateCallUI(callState: state) self.updateCallUI(callState: state)
} }
internal func hasVideoDidChange(call: SignalCall, hasVideo: Bool) { internal func hasLocalVideoDidChange(call: SignalCall, hasLocalVideo: Bool) {
AssertIsOnMainThread() AssertIsOnMainThread()
self.updateCallUI(callState: call.state) self.updateCallUI(callState: call.state)
} }

Loading…
Cancel
Save