Last active
December 19, 2017 15:19
-
-
Save serjooo/d5d7c4009a4a9327d7dda315bc42c86b to your computer and use it in GitHub Desktop.
TwilioVideoManager Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
import TwilioVideo | |
class TwilioVideoManager: NSObject { | |
//MARK: Static instance to class | |
public static let shared = TwilioVideoManager() | |
//MARK: TwilioVideo SDK components | |
fileprivate var room: TVIRoom? | |
fileprivate var camera: TVICameraCapturer? | |
fileprivate var localVideoTrack: TVILocalVideoTrack? | |
fileprivate var localAudioTrack: TVILocalAudioTrack? | |
fileprivate var remoteParticipant: TVIRemoteParticipant? | |
fileprivate var remoteView: TVIVideoView? | |
fileprivate var previewView: TVIVideoView? | |
fileprivate lazy var cameraSource = TVICameraCaptureSource.frontCamera | |
//MARK: Private variables | |
weak var delegate: TwilioVideoManagerDelegate? | |
//MARK: Public helper methods | |
public func setupCamera(previewView: TVIVideoView, remoteView: TVIVideoView? = nil, delegate: TwilioVideoManagerDelegate? = nil) { | |
self.previewView = previewView | |
self.remoteView = remoteView | |
self.delegate = delegate | |
self.camera = TVICameraCapturer(source: cameraSource, delegate: self) | |
let videoConstraints = TVIVideoConstraints.init { (builder) in | |
builder.maxSize = TVIVideoConstraintsSize960x540 | |
builder.minSize = TVIVideoConstraintsSize960x540 | |
builder.aspectRatio = TVIAspectRatio16x9 | |
} | |
localVideoTrack = TVILocalVideoTrack.init(capturer: camera!, enabled: true, constraints: videoConstraints, name: "Camera") | |
localAudioTrack = TVILocalAudioTrack.init(options: nil, enabled: true, name: "Microphone") | |
guard let localVideoTrack = localVideoTrack else { | |
print("Failed to create video Track") | |
return | |
} | |
guard let localAudioTrack = localAudioTrack else { | |
print("Failed to create audio track") | |
return | |
} | |
localVideoTrack.addRenderer(previewView) | |
localAudioTrack.isEnabled = true | |
} | |
public func stopCameraPreview() { | |
self.previewView?.removeFromSuperview() | |
self.previewView = nil | |
} | |
//MARK: Private helper methods | |
fileprivate func cleanupRemoteParticipant() { | |
guard let remoteParticipant = self.remoteParticipant else { | |
self.remoteParticipant = nil | |
return | |
} | |
if remoteParticipant.videoTracks.count > 0 { | |
guard let remoteVideoTrack = remoteParticipant.remoteVideoTracks[0].remoteTrack, let remoteView = remoteView else {return} | |
remoteVideoTrack.removeRenderer(remoteView) | |
//WARNING: make sure you have to remove the remote view from the superview | |
// self.remoteView.removeFromSuperview() | |
// self.remoteView = nil | |
} | |
self.remoteParticipant = nil | |
} | |
//MARK: Public Methods | |
public func flipCamera() { | |
cameraSource = cameraSource == .frontCamera ? .backCameraWide : .frontCamera | |
self.camera?.selectSource(cameraSource) | |
} | |
public func toggleMic() -> Bool { | |
guard let localAudioTrack = localAudioTrack else {return false} | |
localAudioTrack.isEnabled = !localAudioTrack.isEnabled | |
return localAudioTrack.isEnabled | |
} | |
public func connect(roomName: String, accessToken: String) { | |
guard let localVideoTrack = localVideoTrack else { | |
print("Haven't initialized local video Track") | |
return | |
} | |
guard let localAudioTrack = localAudioTrack else { | |
print("Haven't initialized local audio Track") | |
return | |
} | |
// Preparing the connect options with the access token that we fetched (or hardcoded). | |
let connectOptions = TVIConnectOptions.init(token: accessToken) { (builder) in | |
builder.audioTracks = [localAudioTrack] | |
builder.videoTracks = [localVideoTrack] | |
if let preferredAudioCodec = Settings.shared.audioCodec { | |
builder.preferredAudioCodecs = [preferredAudioCodec.rawValue] | |
} | |
if let preferredVideoCodec = Settings.shared.videoCodec { | |
builder.preferredVideoCodecs = [preferredVideoCodec.rawValue] | |
} | |
if let encodingParameters = Settings.shared.getEncodingParameters() { | |
builder.encodingParameters = encodingParameters | |
} | |
// The name of the Room where the Client will attempt to connect to. Please note that if you pass an empty | |
// Room `name`, the Client will create one for you. You can get the name or sid from any connected Room. | |
builder.roomName = roomName | |
} | |
// Connect to the Room using the options we provided. | |
room = TwilioVideo.connect(with: connectOptions, delegate: self) | |
} | |
} | |
// MARK: TVICameraCapturerDelegate | |
extension TwilioVideoManager: TVICameraCapturerDelegate { | |
func cameraCapturer(_ capturer: TVICameraCapturer, didStartWith source: TVICameraCaptureSource) { | |
guard let previewView = previewView else {return} | |
previewView.shouldMirror = (source == .frontCamera) | |
} | |
} | |
// MARK: TVIRoomDelegate | |
extension TwilioVideoManager: TVIRoomDelegate { | |
func didConnect(to room: TVIRoom) { | |
//Maybe add a protocol that it connected? | |
if (room.remoteParticipants.count > 0) { | |
self.remoteParticipant = room.remoteParticipants[0] | |
self.remoteParticipant?.delegate = self | |
delegate?.didConnect?() | |
} | |
} | |
func room(_ room: TVIRoom, didDisconnectWithError error: Error?) { | |
//Maybe add a protocol that it disconnected? | |
self.cleanupRemoteParticipant() | |
self.room = nil | |
delegate?.didDisconnect?() | |
} | |
func room(_ room: TVIRoom, didFailToConnectWithError error: Error) { | |
//Maybe add a protocol that it failed to connect with some error? | |
self.room = nil | |
delegate?.didFailToConnectWithError?(error) | |
} | |
func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) { | |
if (self.remoteParticipant == nil) { | |
self.remoteParticipant = participant | |
self.remoteParticipant?.delegate = self | |
delegate?.didConnect?() | |
} | |
} | |
func room(_ room: TVIRoom, participantDidDisconnect participant: TVIRemoteParticipant) { | |
//Maybe add a protocol that it disconnected? | |
if (self.remoteParticipant == participant) { | |
cleanupRemoteParticipant() | |
delegate?.didDisconnect?() | |
} | |
} | |
} | |
// MARK: TVIRemoteParticipantDelegate | |
extension TwilioVideoManager: TVIRemoteParticipantDelegate { | |
func remoteParticipant(_ participant: TVIRemoteParticipant, publishedVideoTrack publication: TVIRemoteVideoTrackPublication) { | |
// Remote Participant has offered to share the video Track. | |
delegate?.remoteParticipantDidPublishVideoTrack?(true) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, unpublishedVideoTrack publication: TVIRemoteVideoTrackPublication) { | |
// Remote Participant has stopped sharing the video Track. | |
delegate?.remoteParticipantDidPublishVideoTrack?(false) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, publishedAudioTrack publication: TVIRemoteAudioTrackPublication) { | |
// Remote Participant has offered to share the audio Track. | |
delegate?.remoteParticipantDidPublishAudioTrack?(true) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, unpublishedAudioTrack publication: TVIRemoteAudioTrackPublication) { | |
// Remote Participant has stopped sharing the audio Track. | |
delegate?.remoteParticipantDidPublishAudioTrack?(false) | |
} | |
func subscribed(to videoTrack: TVIRemoteVideoTrack, publication: TVIRemoteVideoTrackPublication, for participant: TVIRemoteParticipant) { | |
// We are subscribed to the remote Participant's audio Track. We will start receiving the | |
// remote Participant's video frames now. | |
if (self.remoteParticipant == participant) { | |
if let remoteView = remoteView { | |
videoTrack.addRenderer(remoteView) | |
} | |
delegate?.didSubscribeToVideoTrack?(true) | |
} | |
} | |
func unsubscribed(from videoTrack: TVIRemoteVideoTrack, publication: TVIRemoteVideoTrackPublication, for participant: TVIRemoteParticipant) { | |
// We are unsubscribed from the remote Participant's video Track. We will no longer receive the | |
// remote Participant's video. | |
if (self.remoteParticipant == participant) { | |
//Should I remove from superview and make it nil? Isn't removing the renderer enough? | |
if let remoteView = remoteView { | |
videoTrack.removeRenderer(remoteView) | |
} | |
delegate?.didSubscribeToVideoTrack?(false) | |
// self.remoteView?.removeFromSuperview() | |
// self.remoteView = nil | |
} | |
} | |
func subscribed(to audioTrack: TVIRemoteAudioTrack, publication: TVIRemoteAudioTrackPublication, for participant: TVIRemoteParticipant) { | |
// We are subscribed to the remote Participant's audio Track. We will start receiving the | |
// remote Participant's audio now. | |
delegate?.didSubscribeToAudioTrack?(true) | |
} | |
func unsubscribed(from audioTrack: TVIRemoteAudioTrack, publication: TVIRemoteAudioTrackPublication, for participant: TVIRemoteParticipant) { | |
// We are unsubscribed from the remote Participant's audio Track. We will no longer receive the | |
// remote Participant's audio. | |
delegate?.didSubscribeToAudioTrack?(false) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, enabledVideoTrack publication: TVIRemoteVideoTrackPublication) { | |
//logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) video track") | |
delegate?.didEnableVideoTrack?(true) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, disabledVideoTrack publication: TVIRemoteVideoTrackPublication) { | |
//logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) video track") | |
delegate?.didEnableVideoTrack?(false) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, enabledAudioTrack publication: TVIRemoteAudioTrackPublication) { | |
//logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) audio track") | |
delegate?.didEnableAudioTrack?(true) | |
} | |
func remoteParticipant(_ participant: TVIRemoteParticipant, disabledAudioTrack publication: TVIRemoteAudioTrackPublication) { | |
//logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) audio track") | |
delegate?.didEnableAudioTrack?(false) | |
} | |
} | |
//MARK: TwilioVideoManager Protocol | |
@objc protocol TwilioVideoManagerDelegate: NSObjectProtocol { | |
@objc optional func didConnect() | |
@objc optional func didDisconnect() | |
@objc optional func didFailToConnectWithError(_ error: Error) | |
@objc optional func remoteParticipantDidPublishVideoTrack(_ didPublishVideoTrack: Bool) | |
@objc optional func remoteParticipantDidPublishAudioTrack(_ didPublishAudioTrack: Bool) | |
@objc optional func didSubscribeToVideoTrack(_ didSubscribeVideoTrack: Bool) | |
@objc optional func didSubscribeToAudioTrack(_ didSubscribeAudioTrack: Bool) | |
@objc optional func didEnableVideoTrack(_ didEnableVideoTrack: Bool) | |
@objc optional func didEnableAudioTrack(_ didEnableAudioTrack: Bool) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment