Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
420 lines (328 sloc) 16.9 KB
//
// DesignerViewController.swift
// DesignConsult
//
// Created by Jennifer Aprahamian on 10/20/17.
// Copyright © 2017 Twilio. All rights reserved.
//
import UIKit
import TwilioVideo
class DesignerViewController: UIViewController {
// MARK: View Controller Members
// Configure access token manually for testing, if desired! Create one manually in the console
// at https://www.twilio.com/user/account/video/dev-tools/testing-tools
var accessToken = "TWILIO_ACCESS_TOKEN"
// Configure remote URL to fetch token from
var tokenUrl = "http://localhost:8000/token.php"
// Video SDK components
var room: TVIRoom?
var camera: TVICameraCapturer?
var localVideoTrack: TVILocalVideoTrack?
var localAudioTrack: TVILocalAudioTrack?
var localDataTrack: TVILocalDataTrack?
var remoteParticipant: TVIRemoteParticipant?
var remoteView: TVIVideoView?
var currentObject = ""
var previewView: TVIVideoView?
@IBOutlet weak var chairButton: UIButton!
@IBOutlet weak var iconsContainerView: UIStackView!
@IBOutlet weak var lampButton: UIButton!
@IBOutlet weak var vaseButton: UIButton!
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Design Consultation"
// Add a gesture recognizer for the tap actions so Designer can place items
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
self.view.addGestureRecognizer(tap)
setChair(chairButton)
iconsContainerView.isHidden = true
// Connect to the room
connect()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.previewView?.frame = CGRect(x: self.view.frame.width - 10 - 90,
y: self.view.frame.height + 10 - 160,
width: 90,
height: 160)
}
// Taps from Designer's view are sent to Client as a set of coordinates by sending a message from the local data track
@objc func handleTap(gestureRecognizer: UIGestureRecognizer){
let location = gestureRecognizer.location(in: self.view)
if let localDataTrack = self.localDataTrack {
let message = "\(currentObject) \(location)"
localDataTrack.send(message)
}
}
@IBAction func setChair(_ sender: Any) {
currentObject = "chair"
chairButton.setImage(#imageLiteral(resourceName: "chair-red"), for: .normal)
vaseButton.setImage(#imageLiteral(resourceName: "vase"), for: .normal)
lampButton.setImage(#imageLiteral(resourceName: "lamp"), for: .normal)
}
@IBAction func setLamp(_ sender: Any) {
currentObject = "lamp"
chairButton.setImage(#imageLiteral(resourceName: "chair"), for: .normal)
vaseButton.setImage(#imageLiteral(resourceName: "vase"), for: .normal)
lampButton.setImage(#imageLiteral(resourceName: "lamp-red"), for: .normal)
}
@IBAction func setVase(_ sender: Any) {
currentObject = "vase"
chairButton.setImage(#imageLiteral(resourceName: "chair"), for: .normal)
vaseButton.setImage(#imageLiteral(resourceName: "vase-red"), for: .normal)
lampButton.setImage(#imageLiteral(resourceName: "lamp"), for: .normal)
}
func setupRemoteVideoView() {
// Creating `TVIVideoView` programmatically
self.remoteView = TVIVideoView.init(frame: CGRect.zero, delegate:self)
self.view.insertSubview(self.remoteView!, at: 0)
// `TVIVideoView` supports scaleToFill, scaleAspectFill and scaleAspectFit
// scaleAspectFit is the default mode when you create `TVIVideoView` programmatically.
self.remoteView!.contentMode = .scaleAspectFit;
let centerX = NSLayoutConstraint(item: self.remoteView!,
attribute: NSLayoutAttribute.centerX,
relatedBy: NSLayoutRelation.equal,
toItem: self.view,
attribute: NSLayoutAttribute.centerX,
multiplier: 1,
constant: 0);
self.view.addConstraint(centerX)
let centerY = NSLayoutConstraint(item: self.remoteView!,
attribute: NSLayoutAttribute.centerY,
relatedBy: NSLayoutRelation.equal,
toItem: self.view,
attribute: NSLayoutAttribute.centerY,
multiplier: 1,
constant: 0);
self.view.addConstraint(centerY)
let width = NSLayoutConstraint(item: self.remoteView!,
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: self.view,
attribute: NSLayoutAttribute.width,
multiplier: 1,
constant: 0);
self.view.addConstraint(width)
let height = NSLayoutConstraint(item: self.remoteView!,
attribute: NSLayoutAttribute.height,
relatedBy: NSLayoutRelation.equal,
toItem: self.view,
attribute: NSLayoutAttribute.height,
multiplier: 1,
constant: 0);
self.view.addConstraint(height)
}
// MARK: IBActions
func connect() {
// Configure access token either from server or manually.
// If the default wasn't changed, try fetching from server.
if (accessToken == "TWILIO_ACCESS_TOKEN") {
let urlStringWithRole = tokenUrl + "?identity=Designer"
do {
accessToken = try String(contentsOf:URL(string: urlStringWithRole)!)
} catch {
let message = "Failed to fetch access token"
print(message)
return
}
}
// Prepare local media which we will share with Room Participants.
self.prepareLocalMedia()
// Preparing the connect options with the access token that we fetched (or hardcoded).
let connectOptions = TVIConnectOptions.init(token: accessToken) { (builder) in
// Use the local media that we prepared earlier.
builder.dataTracks = self.localDataTrack != nil ? [self.localDataTrack!] : [TVILocalDataTrack]()
builder.videoTracks = self.localVideoTrack != nil ? [self.localVideoTrack!] : [TVILocalVideoTrack]()
// Use the preferred audio codec
if let preferredAudioCodec = Settings.shared.audioCodec {
builder.preferredAudioCodecs = [preferredAudioCodec.rawValue]
}
builder.preferredVideoCodecs = [TVIVideoCodec.H264.rawValue]
// Use the preferred encoding parameters
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 = "DesignConsult"
}
// Connect to the Room using the options we provided.
room = TwilioVideo.connect(with: connectOptions, delegate: self)
print("Attempting to connect to room")
self.showRoomUI(inRoom: true)
}
func prepareLocalMedia() {
// We will share local audio and video when we connect to the Room.
// Create an audio track.
if (localAudioTrack == nil) {
localAudioTrack = TVILocalAudioTrack.init()
if (localAudioTrack == nil) {
print("Failed to create audio track")
}
}
// Create a data track.
if (localDataTrack == nil) {
localDataTrack = TVILocalDataTrack.init()
}
if (PlatformUtils.isSimulator == false && localVideoTrack == nil) {
// Preview our local camera track in the local video preview view.
camera = TVICameraCapturer(source: .frontCamera, delegate: nil)
localVideoTrack = TVILocalVideoTrack.init(capturer: camera!)
self.previewView = TVIVideoView.init()
self.view.setNeedsLayout()
localVideoTrack?.addRenderer(self.previewView!)
previewView?.shouldMirror = true
self.view.addSubview(self.previewView!)
if (localDataTrack == nil) {
print("Failed to create data track")
}
}
}
// Update our UI based upon if we are in a Room or not
func showRoomUI(inRoom: Bool) {
self.navigationController?.setNavigationBarHidden(inRoom, animated: true)
UIApplication.shared.isIdleTimerDisabled = inRoom
}
func cleanupRemoteParticipant() {
if ((self.remoteParticipant) != nil) {
if ((self.remoteParticipant?.videoTracks.count)! > 0) {
if let remoteVideoTrack = self.remoteParticipant?.remoteVideoTracks[0].remoteTrack {
remoteVideoTrack.removeRenderer(self.remoteView!)
}
self.remoteView?.removeFromSuperview()
self.remoteView = nil
}
}
self.remoteParticipant = nil
}
}
// MARK: TVIRoomDelegate
extension DesignerViewController : TVIRoomDelegate {
func didConnect(to room: TVIRoom) {
// At the moment, this example only supports rendering one Participant at a time.
print("Connected to room \(room.name) as \(String(describing: room.localParticipant?.identity))")
if (room.remoteParticipants.count > 0) {
self.remoteParticipant = room.remoteParticipants[0]
self.remoteParticipant?.delegate = self
}
}
func room(_ room: TVIRoom, didDisconnectWithError error: Error?) {
print("Disconncted from room \(room.name), error = \(String(describing: error))")
self.cleanupRemoteParticipant()
self.room = nil
self.showRoomUI(inRoom: false)
iconsContainerView.isHidden = true
}
func room(_ room: TVIRoom, didFailToConnectWithError error: Error) {
print("Failed to connect to room with error")
self.room = nil
self.showRoomUI(inRoom: false)
}
func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {
if (self.remoteParticipant == nil) {
self.remoteParticipant = participant
self.remoteParticipant?.delegate = self
}
print("Participant \(participant.identity) connected with \(participant.remoteAudioTracks.count) audio and \(participant.remoteVideoTracks.count) video tracks")
}
func room(_ room: TVIRoom, participantDidDisconnect participant: TVIRemoteParticipant) {
if (self.remoteParticipant == participant) {
cleanupRemoteParticipant()
}
print("Room \(room.name), Participant \(participant.identity) disconnected")
}
}
// MARK: TVIRemoteParticipantDelegate
extension DesignerViewController : TVIRemoteParticipantDelegate {
func remoteParticipant(_ participant: TVIRemoteParticipant,
publishedVideoTrack publication: TVIRemoteVideoTrackPublication) {
// Remote Participant has offered to share the video Track.
print("Participant \(participant.identity) published video track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
unpublishedVideoTrack publication: TVIRemoteVideoTrackPublication) {
// Remote Participant has stopped sharing the video Track.
print("Participant \(participant.identity) unpublished video track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
publishedAudioTrack publication: TVIRemoteAudioTrackPublication) {
// Remote Participant has offered to share the audio Track.
print("Participant \(participant.identity) published audio track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
unpublishedAudioTrack publication: TVIRemoteAudioTrackPublication) {
// Remote Participant has stopped sharing the audio Track.
print("Participant \(participant.identity) unpublished audio track")
}
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.
print("Subscribed to video track for Participant \(participant.identity)")
if (self.remoteParticipant == participant) {
setupRemoteVideoView()
videoTrack.addRenderer(self.remoteView!)
}
}
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.
print("Unsubscribed from video track for Participant \(participant.identity)")
if (self.remoteParticipant == participant) {
videoTrack.removeRenderer(self.remoteView!)
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.
print("Subscribed to audio track for Participant \(participant.identity)")
}
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.
print("Unsubscribed from audio track for Participant \(participant.identity)")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
enabledVideoTrack publication: TVIRemoteVideoTrackPublication) {
print("Participant \(participant.identity) enabled video track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
disabledVideoTrack publication: TVIRemoteVideoTrackPublication) {
print("Participant \(participant.identity) disabled video track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
enabledAudioTrack publication: TVIRemoteAudioTrackPublication) {
print("Participant \(participant.identity) enabled audio track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
disabledAudioTrack publication: TVIRemoteAudioTrackPublication) {
print("Participant \(participant.identity) disabled audio track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant, publishedDataTrack publication: TVIRemoteDataTrackPublication) {
print("published data track")
}
func remoteParticipant(_ participant: TVIRemoteParticipant, unpublishedDataTrack publication: TVIRemoteDataTrackPublication) {
print("unpublished data track")
}
}
// MARK: TVIVideoViewDelegate
extension DesignerViewController : TVIVideoViewDelegate {
func videoView(_ view: TVIVideoView, videoDimensionsDidChange dimensions: CMVideoDimensions) {
self.view.setNeedsLayout()
}
func videoViewDidReceiveData(_ view: TVIVideoView) {
if (self.remoteView == view) {
iconsContainerView.isHidden = false
}
}
}