twilio / twilio-video-ios

Programmable Video SDK by Twilio
http://twilio.com/video
Other
65 stars 22 forks source link

no audio or video is transmitted after connected to the room #82

Closed kjoe07 closed 4 years ago

kjoe07 commented 4 years ago

Before filing an issue please check that the issue is not already addressed by the following:

Please ensure that you are not sharing any Personally Identifiable Information(PII) or sensitive account information (API keys, credentials, etc.) when reporting an issue.

Description

after conected two apps no audio or video is shared, after update to adopt lates sdk.

Steps to Reproduce

  1. connect one app
  2. connect the other app.

    Code

// Code that helps reproduce the issue

` // // VideoCallViewController.swift // TheHomeTeachers // // Created by SofticSolutions on 06/04/18. // Copyright © 2018 Bruno Hernandez. All rights reserved. //

import AVKit import UIKit import TwilioVideo import ReplayKit import Sketch import UserNotifications import SwiftyDraw class PruebaViewController: UIViewController, ButtonViewInterface, UNUserNotificationCenterDelegate,CameraSourceDelegate{

var servicio:Servicio!
var accessToken:String!
var roomString:String!

var room: Room?
var localAudioTrack = LocalAudioTrack()
var screenTrack: LocalVideoTrack?
var videoSource: ReplayKitVideoSource?
var participant: RemoteParticipant?
var remoteView: VideoView?

@IBOutlet weak var disconnectButton: UIButton!
@IBOutlet weak var micButton: UIButton!

var sketchView: SketchView!
var buttonView: ButtonView!
var scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width + 50, height: 50))
var pantalla = 1
let useExampleCapturer = false
var avatar:Int!
let kDownscaleBuffers = false
@IBOutlet weak var abajo: NSLayoutConstraint!
@IBOutlet weak var screenView: VideoView!
@IBOutlet weak var btnPizarra: UIButton!
@IBOutlet weak var stack: UIStackView!
// MARK: UIViewController
override func viewDidLoad() {
    super.viewDidLoad()
    RPScreenRecorder.shared().delegate = self
    UIDevice.current.setValue(Int(UIInterfaceOrientation.landscapeRight.rawValue), forKey: "orientation")
    buttonView = ButtonView.instanceFromNib(self)
    sketchView = SketchView(frame: CGRect(x: 0,y: 0,width: UIScreen.main.bounds.width,height: UIScreen.main.bounds.height))
    sketchView.translatesAutoresizingMaskIntoConstraints = false
    UNUserNotificationCenter.current().delegate = self
    screenView.addSubview(sketchView)
    view.addSubview(scrollView)
    scrollView.addSubview(buttonView)
    self.view.layoutSubviews()
    self.disconnectButton.isHidden = true
    self.micButton.isHidden = true
    self.startConference()
    self.dismissKeyboard()
}
override func viewDidAppear(_ animated: Bool) {
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.contentSize = buttonView.frame.size
    scrollView.showsHorizontalScrollIndicator = false
    scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
    scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10).isActive = true
    scrollView.rightAnchor.constraint(equalTo: stack.leftAnchor, constant: 20).isActive = true
    scrollView.heightAnchor.constraint(equalToConstant: 50).isActive = true
    sketchView.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 0).isActive = true
    sketchView.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 0).isActive = true
    sketchView.rightAnchor.constraint(equalTo: screenView.rightAnchor, constant: 0).isActive = true
    sketchView.bottomAnchor.constraint(equalTo: screenView.bottomAnchor, constant: 0).isActive = true
}
private func checkRecordingAvailability() {

}
private func startConference() {
    // Start recording the screen.
    let recorder = RPScreenRecorder.shared()
    recorder.isMicrophoneEnabled = true
    recorder.isCameraEnabled = false
    recorder.discardRecording {
        print("eliminando la grabacion si es que existia alguna")
    }
    if recorder.isRecording{
        recorder.stopCapture(handler: { e in
            print("elimando la captura")
        })
        recorder.stopRecording(handler: { _,e in
            print("stop rrecording",e?.localizedDescription ?? "no error")

        })
    }
    // The source produces either downscaled buffers with smoother motion, or an HD screen recording.
    let options = self.kDownscaleBuffers ? ReplayKitVideoSource.TelecineOptions.p60to24or25or30 : ReplayKitVideoSource.TelecineOptions.disabled
    videoSource = ReplayKitVideoSource(isScreencast: !self.kDownscaleBuffers,telecineOptions: options)
    screenTrack = LocalVideoTrack(source: videoSource!,enabled: true,name: "Screen")

    let videoCodec = Settings.shared.videoCodec ?? Vp8Codec()!
    let (encodingParams, outputFormat) = ReplayKitVideoSource.getParametersForUseCase(codec: videoCodec,isScreencast: !self.kDownscaleBuffers,telecineOptions:options)
    videoSource?.requestOutputFormat(outputFormat)

    recorder.startCapture(handler: { (sampleBuffer, type, error) in
        if error != nil {
            print("Capture error: ", error as Any)
            return
        }

        switch type {
        case RPSampleBufferType.video:
            self.videoSource?.processFrame(sampleBuffer: sampleBuffer)
            break
        case RPSampleBufferType.audioApp:
            break
        case RPSampleBufferType.audioMic:
            // We use `TVIDefaultAudioDevice` to capture and playback audio for conferencing.
            break
        @unknown default:
            fatalError()
        }

    }) { (error) in
        if error != nil {
            print("Screen capture error: ", error as Any)
        } else {
            print("Screen capture started.")
        }
        DispatchQueue.main.async {
            //self.conferenceButton?.isEnabled = true
            if error != nil {
                print("error so no connect")
                self.videoSource = nil
                self.screenTrack = nil
            } else {
                print("send to connect")
                self.connectToRoom(name: self.roomString, encodingParameters: encodingParams)
            }
        }
    }
}

func connectToRoom(name: String, encodingParameters: EncodingParameters) {
    print("prepare to connect ")
    // Preparing the connect options with the access token that we fetched (or hardcoded).
    let connectOptions = ConnectOptions(token: self.accessToken) { (builder) in
        if let audioTracks = self.localAudioTrack{
            builder.audioTracks = [audioTracks]
        }
       // builder.audioTracks = [LocalAudioTrack()!]

        if let videoTrack = self.screenTrack {
            builder.videoTracks = [videoTrack]
        }

        // Use the preferred codecs
        if let preferredAudioCodec = Settings.shared.audioCodec {
            builder.preferredAudioCodecs = [preferredAudioCodec]
        }
        if let preferredVideoCodec = Settings.shared.videoCodec {
            builder.preferredVideoCodecs = [preferredVideoCodec]
        }

        // Use the preferred encoding parameters
        builder.encodingParameters = encodingParameters

        // Use the preferred signaling region
        if let signalingRegion = Settings.shared.signalingRegion {
            builder.region = signalingRegion
        }

        if (!name.isEmpty) {
            builder.roomName = name
        }
    }

    // Connect to the Room using the options we provided.
   // TwilioVideoSDK.setLogLevel(.debug)
    room = TwilioVideoSDK.connect(options: connectOptions, delegate: self) //TwilioVideoSDK.connect(options: connectOptions, delegate: self)
    print("the room is:",room as Any)
    self.showRoomUI(inRoom: true)
}
@IBAction func regresar(_ sender: Any) {
    self.dismiss(animated: true, completion: nil)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler:@escaping (UNNotificationPresentationOptions) -> Void) {
    print("Nueva notificacion \(notification)")
    DispatchQueue.main.async {
        self.disconnect(sender: self)
    }
}

func setupRemoteVideoView() {
    // Creating `TVIVideoView` programmatically
    self.remoteView = VideoView.init(frame: self.screenView.frame, delegate:self)
    self.view.insertSubview(self.remoteView!, at: 0)
    self.remoteView!.contentMode = .scaleAspectFit
    remoteView?.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 0).isActive = true
    remoteView?.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 0).isActive = true
    remoteView?.rightAnchor.constraint(equalTo: screenView.rightAnchor, constant: 0).isActive = true
    //remoteView?.bottomAnchor.constraint(equalTo: screenView.bottomAnchor, constant: 0).isActive = true
    remoteView?.isHidden = false

// let centerX = NSLayoutConstraint(item: self.remoteView!,attribute: NSLayoutConstraint.Attribute.centerX,relatedBy: NSLayoutConstraint.Relation.equal,toItem: self.view,attribute: NSLayoutConstraint.Attribute.centerX,multiplier: 1,constant: 0); // self.view.addConstraint(centerX) // let centerY = NSLayoutConstraint(item: self.remoteView!,attribute: NSLayoutConstraint.Attribute.centerY,relatedBy: NSLayoutConstraint.Relation.equal,toItem: self.view,attribute: NSLayoutConstraint.Attribute.centerY,multiplier: 1,constant: 0); // self.view.addConstraint(centerY) // let width = NSLayoutConstraint(item: self.remoteView!,attribute: NSLayoutConstraint.Attribute.width,relatedBy: NSLayoutConstraint.Relation.equal,toItem: self.view,attribute: NSLayoutConstraint.Attribute.width,multiplier: 1,constant: 0); // self.view.addConstraint(width) // let height = NSLayoutConstraint(item: self.remoteView!,attribute: NSLayoutConstraint.Attribute.height,relatedBy: NSLayoutConstraint.Relation.equal,toItem: self.view,attribute: NSLayoutConstraint.Attribute.height,multiplier: 1,constant: 0); // self.view.addConstraint(height) }

func teardownLocalMedia() { if let renderer = remoteView { screenTrack?.removeRenderer(renderer) } screenTrack = nil }

@IBAction func disconnect(sender: AnyObject) {
    let recorder = RPScreenRecorder.shared()
    recorder.stopCapture { (captureError) in
        if let error = captureError {
            print("Screen capture stop error: ", error as Any)
        } else {
            print("Screen capture stopped.")
            DispatchQueue.main.async {
                //self.screenTrack?.removeRenderer(remoteView)
                if let renderer = self.remoteView {
                    self.screenTrack?.removeRenderer(renderer)
                }
                self.videoSource = nil
                self.screenTrack = nil

            }
        }
    }
    logMessage(messageText: "Attempting to disconnect from room \(room?.name ?? "Not avalible")")
    if let room = self.room,
        room.state == .connected {
        room.disconnect()
    } else {
        room = nil
    }

   let ws = WebServiceClient()
   let pref = UserDefaults.standard
   print(self.servicio.id)
   showActivityIndicator()
   ws.finalizar(token: pref.object(forKey: "token") as! String, idServicio: self.servicio.id, completion: { [weak self] data in
       DispatchQueue.main.async {
           guard let self = self else {return}
           self.hideActivityIndicator()
           let viewController = self.storyboard?.instantiateViewController(withIdentifier: "calificacionViewController") as! CalificarViewController
           viewController.servicio = self.servicio
           viewController.avatar = self.avatar
           self.present(viewController, animated: true, completion: nil)
       }
   })

}

@IBAction func toggleMic(sender: AnyObject) {
    if (self.localAudioTrack != nil) {
    self.localAudioTrack?.isEnabled = !(self.localAudioTrack!.isEnabled)
    if (self.localAudioTrack?.isEnabled == true) {
            self.micButton.setTitle("Mute", for: .normal)
        } else {
            self.micButton.setTitle("Unmute", for: .normal)
        }
    }
}

// MARK: Private
// Update our UI based upon if we are in a Room or not
func showRoomUI(inRoom: Bool) {
    self.micButton.isHidden = inRoom
    self.disconnectButton.isHidden = inRoom
    UIApplication.shared.isIdleTimerDisabled = inRoom
}

@objc func dismissKeyboard() {
}
func renderRemoteParticipant(participant : RemoteParticipant) -> Bool {
    let videoPublications = participant.remoteVideoTracks
    for publication in videoPublications {
        if let subscribedVideoTrack = publication.remoteTrack,
            publication.isTrackSubscribed {
            setupRemoteVideoView()
            subscribedVideoTrack.addRenderer(self.remoteView!)
            self.participant = participant
            return true
        }
    }
    return false
}
func renderRemoteParticipants(participants : Array<RemoteParticipant>) {
    for participant in participants {
        // Find the first renderable track.
        if participant.remoteVideoTracks.count > 0,renderRemoteParticipant(participant: participant) {
            break
        }
    }
}
func cleanupRemoteParticipant() {
    if self.participant != nil {
        self.remoteView?.removeFromSuperview()
        self.remoteView = nil
        self.participant = nil
    }
    self.participant = nil

    let viewController = self.storyboard?.instantiateViewController(withIdentifier: "calificacionViewController") as! CalificarViewController
    viewController.servicio = self.servicio
    viewController.avatar = self.avatar
    self.present(viewController, animated: true, completion: nil)
}

func logMessage(messageText: String) {
    print(messageText)
}

@IBAction func btnPantallaProfe() {

    print("Pantalla: \(self.pantalla)")

    if self.pantalla == 0 {
        self.pantalla = 1
        self.remoteView?.isHidden = true
        self.screenView.isHidden = false
        self.scrollView.isHidden = false
        self.btnPizarra.setTitle("Ver profesor", for: .normal)

    } else {
        self.pantalla = 0
        self.remoteView?.isHidden = false
        self.screenView.isHidden = true
        self.scrollView.isHidden = true
        self.btnPizarra.setTitle("Mi pizarron", for: .normal)

    }
}

}

// MARK: UITextFieldDelegate extension PruebaViewController : UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { //self.connect(sender: textField) return true } }

// MARK: TVIRoomDelegate extension PruebaViewController : RoomDelegate { func roomDidConnect(room: Room) { // The Local Participant if let localParticipant = room.localParticipant { print("Local identity (localParticipant.identity)") } let participants = room.remoteParticipants for remoteParticipant in room.remoteParticipants { remoteParticipant.delegate = self } print("Number of connected Participants (participants.count)") } func roomDidDisconnect(room: Room, error: Error?) { logMessage(messageText: "Disconncted from room (room.name), error = (String(describing: error))") self.cleanupRemoteParticipant() self.room = nil self.showRoomUI(inRoom: false) } func roomDidStartRecording(room: Room) { print("recording") }

func roomDidFailToConnect(room: Room, error: Error) {
    logMessage(messageText: "Failed to connect to room with error")
    self.room = nil
    self.showRoomUI(inRoom: false)
}

func participantDidConnect(room: Room, participant: RemoteParticipant) {
    participant.delegate = self
     print("Participant connected: \(participant.identity)")
}
func participantDidDisconnect(room: Room, participant: RemoteParticipant) {
    logMessage(messageText: "Room \(room.name), Participant \(participant.identity) disconnected")
}

}

// MARK: TVIParticipantDelegate extension PruebaViewController : RemoteParticipantDelegate { func remoteParticipantDidPublishVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) { // Remote Participant has offered to share the video Track. logMessage(messageText: "Participant (participant.identity) published (publication.trackName) video track") }

func remoteParticipantDidUnpublishVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
    // Remote Participant has stopped sharing the video Track.
    logMessage(messageText: "Participant \(participant.identity) unpublished \(publication.trackName) video track")
}

func remoteParticipantDidPublishAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
    // Remote Participant has offered to share the audio Track.
    logMessage(messageText: "Participant \(participant.identity) published \(publication.trackName) audio track")
}

func remoteParticipantDidUnpublishAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
    // Remote Participant has stopped sharing the audio Track.
    logMessage(messageText: "Participant \(participant.identity) unpublished \(publication.trackName) audio track")
}

func didSubscribeToVideoTrack(videoTrack: RemoteVideoTrack, publication: RemoteVideoTrackPublication, participant: RemoteParticipant) {
    // The LocalParticipant is subscribed to the RemoteParticipant's video Track. Frames will begin to arrive now.
    logMessage(messageText: "Subscribed to \(publication.trackName) video track for Participant \(participant.identity)")
    if (self.participant == nil) {
        _ = renderRemoteParticipant(participant: participant)
    }
}

func didUnsubscribeFromVideoTrack(videoTrack: RemoteVideoTrack, publication: RemoteVideoTrackPublication, participant: RemoteParticipant) {
    logMessage(messageText: "Unsubscribed from \(publication.trackName) video track for Participant \(participant.identity)")
    if self.participant == participant {
        cleanupRemoteParticipant()
        // Find another Participant video to render, if possible.
        if var remainingParticipants = room?.remoteParticipants,
            let index = remainingParticipants.firstIndex(of: participant) {
            remainingParticipants.remove(at: index)
            renderRemoteParticipants(participants: remainingParticipants)
        }
    }
}

func didSubscribeToAudioTrack(audioTrack: RemoteAudioTrack, publication: RemoteAudioTrackPublication, participant: RemoteParticipant) {
    // We are subscribed to the remote Participant's audio Track. We will start receiving the remote Participant's audio now.
    logMessage(messageText: "Subscribed to \(publication.trackName) audio track for Participant \(participant.identity)")
}

func didUnsubscribeFromAudioTrack(audioTrack: RemoteAudioTrack, publication: RemoteAudioTrackPublication, participant: RemoteParticipant) {
    // We are unsubscribed from the remote Participant's audio Track. We will no longer receive the remote Participant's audio.
    logMessage(messageText: "Unsubscribed from \(publication.trackName) audio track for Participant \(participant.identity)")
}

func remoteParticipantDidEnableVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
    logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) video track")
}

func remoteParticipantDidDisableVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
    logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) video track")
}

func remoteParticipantDidEnableAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
    logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) audio track")
}

func remoteParticipantDidDisableAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
    logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) audio track")
}

func didFailToSubscribeToAudioTrack(publication: RemoteAudioTrackPublication, error: Error, participant: RemoteParticipant) {
    logMessage(messageText: "FailedToSubscribe \(publication.trackName) audio track, error = \(String(describing: error))")
}

func didFailToSubscribeToVideoTrack(publication: RemoteVideoTrackPublication, error: Error, participant: RemoteParticipant) {
    logMessage(messageText: "FailedToSubscribe \(publication.trackName) video track, error = \(String(describing: error))")
}

}

// MARK: TVIVideoViewDelegate extension PruebaViewController : VideoViewDelegate { func videoViewDimensionsDidChange(view: VideoView, dimensions: CMVideoDimensions) { self.view.setNeedsLayout() } func videoViewDidReceiveData(view: VideoView) { if (view == remoteView) { remoteView?.isHidden = false self.view.setNeedsLayout() } } }

// MARK: TVICameraCapturerDelegate //extension PruebaViewController : TVICameraCapturerDelegate { // func cameraCapturer(_ capturer: TVICameraCapturer, didStartWith source: TVICameraCaptureSource) { // self.previewView.shouldMirror = (source == .frontCamera) // } //}

extension PruebaViewController { func tapPenButton() { sketchView.drawTool = .pen }

func tapEraserButton() {
    sketchView.drawTool = .eraser
}

func tapUndoButton() {
    sketchView.undo()
}

func tapRedoButton() {
    sketchView.redo()
}

func tapClearButton() {
    sketchView.clear()
}

func tapPaletteButton() {
    // Black
    let blackAction = UIAlertAction(title: "Black", style: .default) { _ in
        self.sketchView.lineColor = .black
    }
    // Blue
    let blueAction = UIAlertAction(title: "Blue", style: .default) { _ in
        self.sketchView.lineColor = .blue
    }
    // Red
    let redAction = UIAlertAction(title: "Red", style: .default) { _ in
        self.sketchView.lineColor = .red
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a color", message: nil, preferredStyle: .alert)
    alertController.addAction(blackAction)
    alertController.addAction(blueAction)
    alertController.addAction(redAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

func tapStampButton() {
    // Heart
    let heartAction = UIAlertAction(title: "Heart", style: .default) { _ in
        self.changeStampMode(stampName: "Heart")
    }
    // Star
    let starAction = UIAlertAction(title: "Star", style: .default) { _ in
        self.changeStampMode(stampName: "Star")
    }
    // Smile
    let smileAction = UIAlertAction(title: "Smile", style: .default) { _ in
        self.changeStampMode(stampName: "Smile")
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a stamp", message: nil, preferredStyle: .alert)
    alertController.addAction(heartAction)
    alertController.addAction(starAction)
    alertController.addAction(smileAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

private func changeStampMode(stampName: String) {
    sketchView.stampImage = UIImage(named: stampName)
    sketchView.drawTool = .stamp
}

func tapFigureButton() {
    // Line
    let lineAction = UIAlertAction(title: "Line", style: .default) { _ in
        self.sketchView.drawTool = .line
    }
    // Arrow
    let arrowAction = UIAlertAction(title: "Arrow", style: .default) { _ in
        self.sketchView.drawTool = .arrow
    }
    // Rect
    let rectAction = UIAlertAction(title: "Rect", style: .default) { _ in
        self.sketchView.drawTool = .rectangleStroke
    }
    // Rectfill
    let rectFillAction = UIAlertAction(title: "Rect(Fill)", style: .default) { _ in
        self.sketchView.drawTool = .rectangleFill
    }
    // Ellipse
    let ellipseAction = UIAlertAction(title: "Ellipse", style: .default) { _ in
        self.sketchView.drawTool = .ellipseStroke
    }
    // EllipseFill
    let ellipseFillAction = UIAlertAction(title: "Ellipse(Fill)", style: .default) { _ in
        self.sketchView.drawTool = .ellipseFill
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a figure", message: nil, preferredStyle: .alert)
    alertController.addAction(lineAction)
    alertController.addAction(arrowAction)
    alertController.addAction(rectAction)
    alertController.addAction(rectFillAction)
    alertController.addAction(ellipseAction)
    alertController.addAction(ellipseFillAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

func tapFilterButton() {
    // Normal
    let normalAction = UIAlertAction(title: "Normal", style: .default) { _ in
        self.sketchView.drawingPenType = .normal
    }
    // Blur
    let blurAction = UIAlertAction(title: "Blur", style: .default) { _ in
        self.sketchView.drawingPenType = .blur
    }
    // Neon
    let neonAction = UIAlertAction(title: "Neon", style: .default) { _ in
        self.sketchView.drawingPenType = .neon
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a filter type", message: nil, preferredStyle: .alert)
    alertController.addAction(normalAction)
    alertController.addAction(blurAction)
    alertController.addAction(neonAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

func tapCameraButton() {
    // Camera
    let cameraAction = UIAlertAction(title: "Camera", style: .default) { _ in
        self.setImageFromCamera()
    }
    // Gallery
    let galleryAction = UIAlertAction(title: "Gallery", style: .default) { _ in
        self.setImageFromGallery()
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a Picture", message: nil, preferredStyle: .alert)
    alertController.addAction(cameraAction)
    alertController.addAction(galleryAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

private func setImageFromCamera() {
    PhotoRequestManager.requestPhotoFromCamera(self){ [weak self] result in
        switch result {
        case .success(let image):
            self?.sketchView.loadImage(image: image)
        case .faild:
            print("failed")
        case .cancel:
            break
        }
    }
}

private func setImageFromGallery() {
    PhotoRequestManager.requestPhotoLibrary(self){ [weak self] result in
        switch result {
        case .success(let image):
            self?.sketchView.loadImage(image: image)
        case .faild:
            print("failed")
        case .cancel:
            break
        }
    }
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    if(UIDevice.current.userInterfaceIdiom == .pad){
        return .landscapeRight
    }else {
       return .landscapeRight
    }
}

} extension PruebaViewController: RPScreenRecorderDelegate { func screenRecorderDidChangeAvailability(_ screenRecorder: RPScreenRecorder) { if Thread.isMainThread { // Assume we will get an error raised if we are actively broadcasting / capturing and access is "stolen". if (screenTrack == nil) { checkRecordingAvailability() } } else { DispatchQueue.main.async { self.screenRecorderDidChangeAvailability(screenRecorder) } } } } `

  1. conected the second app with this view controller `// // VideoCallViewController.swift // TheHomeTeachers // // Created by SofticSolutions on 06/04/18. // Copyright © 2018 Bruno Hernandez. All rights reserved. // import AVKit import UIKit import TwilioVideo import ReplayKit import Sketch

class VideoCallViewController: UIViewController, ButtonViewInterface{ // MARK: View Controller Members

// Configure access token for testing. Create one manually in the console
// at https://www.twilio.com/console/video/runtime/testing-tools
//var accessToken:String!
//var roomString:String!

var servicio:Servicio!
var accessToken:String!
var roomString:String!
var idServicio:Int!

// Configure remote URL to fetch token from
//var tokenUrl = "https://video.twilio.com/v1/Rooms/"

// Video SDK components
var room: Room?
//var camera: TVICameraCapturer?
//var localVideoTrack: LocalVideoTrack?
var localAudioTrack = LocalAudioTrack()
var screenTrack: LocalVideoTrack?
var videoSource: ReplayKitVideoSource?
var participant: RemoteParticipant?
var remoteView: VideoView!
let kDownscaleBuffers = false
// MARK: UI Element Outlets and handles

// `TVIVideoView` created from a storyboard

// @IBOutlet weak var previewView: VideoView! @IBOutlet weak var disconnectButton: UIButton! @IBOutlet weak var micButton: UIButton! @IBOutlet weak var stack: UIStackView! @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var sketchView: SketchView! var buttonView: ButtonView! // var scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width + 50, height: 50)) var screenCapturer: VideoSource?//TVIVideoCapturer? @IBOutlet weak var screenView: UIView! var pantalla = 1 let useExampleCapturer = false

@IBOutlet weak var btnPizarra: UIButton!

// MARK: UIViewController
override func viewDidLoad() {
    super.viewDidLoad()
    //self.view.translatesAutoresizingMaskIntoConstraints = false
    RPScreenRecorder.shared().delegate = self

// sketchView = SketchView(frame: CGRect(x: 0,y: 0,width: UIScreen.main.bounds.width,height: UIScreen.main.bounds.height - 60)) // sketchView.translatesAutoresizingMaskIntoConstraints = false // screenView.addSubview(sketchView)

    print("room \(self.roomString!)")
    print("token \(self.accessToken!)")

    UIDevice.current.setValue(Int(UIInterfaceOrientation.landscapeRight.rawValue), forKey: "orientation")
   // buttonView = ButtonView.instanceFromNib(self)
    //view.addSubview(scrollView)
   // scrollView.addSubview(buttonView)

// scrollView.contentSize = buttonView.frame.size // scrollView.showsHorizontalScrollIndicator = false // scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5).isActive = true // scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true // scrollView.heightAnchor.constraint(equalToConstant: 50).isActive = true //scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true //self.startPreview() // Disconnect and mic button will be displayed when the Client is connected to a Room. self.disconnectButton.isHidden = true self.micButton.isHidden = true

    let tap = UITapGestureRecognizer(target: self, action: #selector(VideoCallViewController.dismissKeyboard))
    self.view.addGestureRecognizer(tap)
    startConference()
    self.dismissKeyboard()
    setupRemoteVideoView()
}

@IBAction func regresar(_ sender: Any) {
    self.dismiss(animated: true, completion: nil)
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    if(UIDevice.current.userInterfaceIdiom == .pad){
        return .landscapeRight
    }else {
        return .landscapeRight
    }
}

func cargarPantalla(){

    //sketchView = SketchView(frame: CGRect(x: 0,y: 0,width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))

// sketchView.translatesAutoresizingMaskIntoConstraints = false //screenView.addSubview(sketchView) // view.addSubview(scrollView) // scrollView.addSubview(buttonView) // scrollView.contentSize = buttonView.frame.size // scrollView.showsHorizontalScrollIndicator = false // scrollView.translatesAutoresizingMaskIntoConstraints = false // scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 5).isActive = true // scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10).isActive = true // scrollView.rightAnchor.constraint(equalTo: stack.rightAnchor, constant: 20).isActive = true // scrollView.heightAnchor.constraint(equalToConstant: 50).isActive = true // sketchView.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 0).isActive = true // sketchView.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 0).isActive = true // sketchView.rightAnchor.constraint(equalTo: screenView.rightAnchor, constant: 0).isActive = true // sketchView.bottomAnchor.constraint(equalTo: screenView.bottomAnchor, constant: 0).isActive = true // scrollView.frame.origin.x = 0 // scrollView.frame.origin.y = UIScreen.main.bounds.height - buttonView.frame.size.height - 24 } private func startConference() { // Start recording the screen. let recorder = RPScreenRecorder.shared() recorder.isMicrophoneEnabled = true recorder.isCameraEnabled = false recorder.discardRecording { print("eliminando la grabacion si es que existia alguna") } if recorder.isRecording{ recorder.stopCapture(handler: { e in print("elimando la captura") }) recorder.stopRecording(handler: { _,e in print("stop rrecording",e?.localizedDescription ?? "no error")

        })
    }

    // The source produces either downscaled buffers with smoother motion, or an HD screen recording.
    let options = self.kDownscaleBuffers ? ReplayKitVideoSource.TelecineOptions.p60to24or25or30 : ReplayKitVideoSource.TelecineOptions.disabled
    videoSource = ReplayKitVideoSource(isScreencast: !self.kDownscaleBuffers,telecineOptions: options)
    screenTrack = LocalVideoTrack(source: videoSource!,enabled: true,name: "Screen")

    let videoCodec = Settings.shared.videoCodec ?? Vp8Codec()!
    let (encodingParams, outputFormat) = ReplayKitVideoSource.getParametersForUseCase(codec: videoCodec,isScreencast: !self.kDownscaleBuffers,telecineOptions:options)
    videoSource?.requestOutputFormat(outputFormat)

    recorder.startCapture(handler: { (sampleBuffer, type, error) in
        if error != nil {
            print("Capture error: ", error?.localizedDescription ?? "No hay error o no tiene descripcion")
            return
        }

        switch type {
        case RPSampleBufferType.video:
            self.videoSource?.processFrame(sampleBuffer: sampleBuffer)
            break
        case RPSampleBufferType.audioApp:
            break
        case RPSampleBufferType.audioMic:
            // We use `TVIDefaultAudioDevice` to capture and playback audio for conferencing.
            break
        @unknown default:
            fatalError()
        }

    }) { (error) in
        if error != nil {
            print("Screen capture error: ", error as Any)
        } else {
            print("Screen capture started.")
        }
        DispatchQueue.main.async {
            self.disconnectButton.isHidden = true
            self.micButton.isHidden = true
            if error != nil {
                self.videoSource = nil
                self.screenTrack = nil
            } else {
                self.connectToRoom(name: self.roomString, encodingParameters: encodingParams)
            }
        }
    }
}
private func connectToRoom(name: String, encodingParameters: EncodingParameters) {
    // Preparing the connect options with the access token that we fetched (or hardcoded).
    let connectOptions = ConnectOptions(token: accessToken) { (builder) in

// builder.audioTracks = [LocalAudioTrack()!] if let audioTracks = self.localAudioTrack{ builder.audioTracks = [audioTracks] } if let videoTrack = self.screenTrack { builder.videoTracks = [videoTrack] }

         // Use the preferred codecs
        if let preferredAudioCodec = Settings.shared.audioCodec {
            builder.preferredAudioCodecs = [preferredAudioCodec]
        }
        if let preferredVideoCodec = Settings.shared.videoCodec {
            builder.preferredVideoCodecs = [preferredVideoCodec]
        }

        // Use the preferred encoding parameters
        builder.encodingParameters = encodingParameters

        // Use the preferred signaling region
        if let signalingRegion = Settings.shared.signalingRegion {
            builder.region = signalingRegion
        }

        if (!name.isEmpty) {
            builder.roomName = name
        }
    }

    // Connect to the Room using the options we provided.
    room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)
}
func setupRemoteVideoView() {
    // Creating `TVIVideoView` programmatically
    self.remoteView = VideoView.init(frame: CGRect.zero, delegate:self)
    //remoteView.backgroundColor = .green
    //remoteView.delegate = self
   // remoteView?.translatesAutoresizingMaskIntoConstraints = false
    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
    remoteView?.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 0).isActive = true
    remoteView?.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 0).isActive = true
    remoteView?.rightAnchor.constraint(equalTo: screenView.rightAnchor, constant: 0).isActive = true
    remoteView?.bottomAnchor.constraint(equalTo: screenView.bottomAnchor, constant: 0).isActive = true
    remoteView?.isHidden = true

// let centerX = NSLayoutConstraint(item: self.remoteView!,attribute: NSLayoutConstraint.Attribute.centerX,relatedBy: NSLayoutConstraint.Relation.equal,toItem: self.view,attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0); // self.view.addConstraint(centerX) // let centerY = NSLayoutConstraint(item: self.remoteView!, // attribute: NSLayoutConstraint.Attribute.centerY, // relatedBy: NSLayoutConstraint.Relation.equal, // toItem: self.view, // attribute: NSLayoutConstraint.Attribute.centerY, // multiplier: 1, // constant: 0); // self.view.addConstraint(centerY) // let width = NSLayoutConstraint(item: self.remoteView!, // attribute: NSLayoutConstraint.Attribute.width, // relatedBy: NSLayoutConstraint.Relation.equal, // toItem: self.view, // attribute: NSLayoutConstraint.Attribute.width, // multiplier: 1, // constant: 0); // self.view.addConstraint(width) // let height = NSLayoutConstraint(item: self.remoteView!, // attribute: NSLayoutConstraint.Attribute.height, // relatedBy: NSLayoutConstraint.Relation.equal, // toItem: self.view, // attribute: NSLayoutConstraint.Attribute.height, // multiplier: 1, // constant: 0); // self.view.addConstraint(height) }

@IBAction func disconnect(sender: AnyObject) {
    self.room?.disconnect()
    let ws = WebServiceClient()
    let pref = UserDefaults()
    print(self.idServicio ?? 0)
    ws.finalizar(token: pref.string(forKey: "token") ?? "", idServicio: self.idServicio, completion: { data in
        DispatchQueue.main.async {
            DispatchQueue.main.async {
                let res = data.object(forKey: "result") as? Int
                if res ?? 0 > 0 {
                    pref.removeObject(forKey: "idServicio")
                    pref.removeObject(forKey: "serviceRoom")
                    pref.removeObject(forKey: "tipoServicio")
                }
                let viewController = self.storyboard?.instantiateViewController(withIdentifier: "calificacionViewController") as! CalificacionViewController
                viewController.servicio = self.servicio
                viewController.modalPresentationStyle = .fullScreen
                self.present(viewController, animated: true, completion: nil)
            }
        }
    })        
}

@IBAction func toggleMic(sender: AnyObject) {
    if (self.localAudioTrack != nil) {
        self.localAudioTrack?.isEnabled = !(self.localAudioTrack?.isEnabled ?? false)
        // Update the button title
        if (self.localAudioTrack?.isEnabled == true) {
            self.micButton.setTitle("Mute", for: .normal)
        } else {
            self.micButton.setTitle("Unmute", for: .normal)
        }
    }
}
func renderRemoteParticipant(participant : RemoteParticipant) -> Bool {
    // This example renders the first subscribed RemoteVideoTrack from the RemoteParticipant.
    let videoPublications = participant.remoteVideoTracks
    for publication in videoPublications {
        if let subscribedVideoTrack = publication.remoteTrack,
            publication.isTrackSubscribed {
            setupRemoteVideoView()
            subscribedVideoTrack.addRenderer(self.remoteView!)
            self.participant = participant
            return true
        }
    }
    return false
}
func renderRemoteParticipants(participants : Array<RemoteParticipant>) {
    for participant in participants {
        // Find the first renderable track.
        if participant.remoteVideoTracks.count > 0,renderRemoteParticipant(participant: participant) {
            break
        }
    }
}
// MARK: Private

// Update our UI based upon if we are in a Room or not
func showRoomUI(inRoom: Bool) {
    DispatchQueue.main.async {
        self.micButton.isHidden = inRoom
        self.disconnectButton.isHidden = inRoom
         UIApplication.shared.isIdleTimerDisabled = inRoom
    }

}

@objc func dismissKeyboard() {
    /*if (self.roomTextField.isFirstResponder) {
     self.roomTextField.resignFirstResponder()
     }*/
}

func cleanupRemoteParticipant() {
    DispatchQueue.main.async {
        if ((self.participant) != nil) {
            if ((self.participant?.videoTracks.count ?? 0) > 0) {
                self.participant?.videoTracks[0].videoTrack?.removeRenderer(self.remoteView!)//removeRenderer(self.remoteView!)
                self.remoteView?.removeFromSuperview()
                self.remoteView = nil
            }
        }
        self.participant = nil
    }

}

func logMessage(messageText: String) {
    print(messageText)
}

@IBAction func btnPantallaProfe() {

    print("Pantalla: \(self.pantalla)")

    if self.pantalla == 0 {
        self.pantalla = 1
        self.remoteView?.isHidden = true
        self.screenView.isHidden = false
        //self.scrollView.isHidden = false
        self.btnPizarra.setTitle("Alumno", for: .normal)
    } else {
        self.pantalla = 0
        self.remoteView?.isHidden = false
        self.screenView.isHidden = true
        //self.scrollView.isHidden = true
        self.btnPizarra.setTitle("Pizarron", for: .normal)
    }
}

}

// MARK: UITextFieldDelegate extension VideoCallViewController : UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { //self.connect(sender: textField) return true } }

// MARK: TVIRoomDelegate extension VideoCallViewController : RoomDelegate { func roomDidConnect(room: Room) { // The Local Participant if let localParticipant = room.localParticipant { print("Local identity (localParticipant.identity)") } // Connected participants let participants = room.remoteParticipants for remoteParticipant in room.remoteParticipants { remoteParticipant.delegate = self } print("Number of connected Participants (participants.count)") } func roomDidDisconnect(room: Room, error: Error?) { logMessage(messageText: "Disconncted from room (room.name), error = (String(describing: error))") self.cleanupRemoteParticipant() self.room = nil self.showRoomUI(inRoom: false) } func roomDidFailToConnect(room: Room, error: Error) { print("Failed to connect to room with error",error.localizedDescription) self.room = nil self.showRoomUI(inRoom: false) } func participantDidConnect(room: Room, participant: RemoteParticipant) { participant.delegate = self print("Participant connected: (participant.identity)") } func participantDidDisconnect(room: Room, participant: RemoteParticipant) { logMessage(messageText: "Room (room.name), Participant (participant.identity) disconnected") }

}

// MARK: TVIParticipantDelegate extension VideoCallViewController : RemoteParticipantDelegate { func remoteParticipantDidPublishVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) { // Remote Participant has offered to share the video Track. logMessage(messageText: "Participant (participant.identity) published (publication.trackName) video track") }

    func remoteParticipantDidUnpublishVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
        // Remote Participant has stopped sharing the video Track.
        logMessage(messageText: "Participant \(participant.identity) unpublished \(publication.trackName) video track")
    }

    func remoteParticipantDidPublishAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
        // Remote Participant has offered to share the audio Track.
        logMessage(messageText: "Participant \(participant.identity) published \(publication.trackName) audio track")
    }

    func remoteParticipantDidUnpublishAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
        // Remote Participant has stopped sharing the audio Track.
        logMessage(messageText: "Participant \(participant.identity) unpublished \(publication.trackName) audio track")
    }

    func didSubscribeToVideoTrack(videoTrack: RemoteVideoTrack, publication: RemoteVideoTrackPublication, participant: RemoteParticipant) {
        // The LocalParticipant is subscribed to the RemoteParticipant's video Track. Frames will begin to arrive now.
        logMessage(messageText: "Subscribed to \(publication.trackName) video track for Participant \(participant.identity)")
        if (self.participant == nil) {
            _ = renderRemoteParticipant(participant: participant)
        }
    }

    func didUnsubscribeFromVideoTrack(videoTrack: RemoteVideoTrack, publication: RemoteVideoTrackPublication, participant: RemoteParticipant) {
        logMessage(messageText: "Unsubscribed from \(publication.trackName) video track for Participant \(participant.identity)")
        if self.participant == participant {
            cleanupRemoteParticipant()
            // Find another Participant video to render, if possible.
            if var remainingParticipants = room?.remoteParticipants,
                let index = remainingParticipants.firstIndex(of: participant) {
                remainingParticipants.remove(at: index)
                renderRemoteParticipants(participants: remainingParticipants)
            }
        }
    }

    func didSubscribeToAudioTrack(audioTrack: RemoteAudioTrack, publication: RemoteAudioTrackPublication, participant: RemoteParticipant) {
        // We are subscribed to the remote Participant's audio Track. We will start receiving the remote Participant's audio now.
        logMessage(messageText: "Subscribed to \(publication.trackName) audio track for Participant \(participant.identity)")
    }

    func didUnsubscribeFromAudioTrack(audioTrack: RemoteAudioTrack, publication: RemoteAudioTrackPublication, participant: RemoteParticipant) {
        // We are unsubscribed from the remote Participant's audio Track. We will no longer receive the remote Participant's audio.
        logMessage(messageText: "Unsubscribed from \(publication.trackName) audio track for Participant \(participant.identity)")
    }

    func remoteParticipantDidEnableVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
        logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) video track")
    }

    func remoteParticipantDidDisableVideoTrack(participant: RemoteParticipant, publication: RemoteVideoTrackPublication) {
        logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) video track")
    }

    func remoteParticipantDidEnableAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
        logMessage(messageText: "Participant \(participant.identity) enabled \(publication.trackName) audio track")
    }

    func remoteParticipantDidDisableAudioTrack(participant: RemoteParticipant, publication: RemoteAudioTrackPublication) {
        logMessage(messageText: "Participant \(participant.identity) disabled \(publication.trackName) audio track")
    }

    func didFailToSubscribeToAudioTrack(publication: RemoteAudioTrackPublication, error: Error, participant: RemoteParticipant) {
        logMessage(messageText: "FailedToSubscribe \(publication.trackName) audio track, error = \(String(describing: error))")
    }

    func didFailToSubscribeToVideoTrack(publication: RemoteVideoTrackPublication, error: Error, participant: RemoteParticipant) {
        logMessage(messageText: "FailedToSubscribe \(publication.trackName) video track, error = \(String(describing: error))")
    }

}

// MARK: TVIVideoViewDelegate extension VideoCallViewController : VideoViewDelegate { func videoViewDimensionsDidChange(view: VideoView, dimensions: CMVideoDimensions) { self.view.setNeedsLayout() } }

// MARK: TVICameraCapturerDelegate // extension VideoCallViewController : TVICameraCapturerDelegate { // func cameraCapturer(_ capturer: TVICameraCapturer, didStartWith source: TVICameraCaptureSource) { // self.previewView.shouldMirror = (source == .frontCamera) // } // }

extension VideoCallViewController { @IBAction func tapPenButton() { sketchView.drawTool = .pen }

@IBAction func tapEraserButton() {
    sketchView.drawTool = .eraser
}

@IBAction func tapUndoButton() {
    sketchView.undo()
}

@IBAction func tapRedoButton() {
    sketchView.redo()
}

@IBAction func tapClearButton() {
    sketchView.clear()
}

@IBAction func tapPaletteButton() {
    // Black
    let blackAction = UIAlertAction(title: "Black", style: .default) { _ in
        self.sketchView.lineColor = .black
    }
    // Blue
    let blueAction = UIAlertAction(title: "Blue", style: .default) { _ in
        self.sketchView.lineColor = .blue
    }
    // Red
    let redAction = UIAlertAction(title: "Red", style: .default) { _ in
        self.sketchView.lineColor = .red
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a color", message: nil, preferredStyle: .alert)
    alertController.addAction(blackAction)
    alertController.addAction(blueAction)
    alertController.addAction(redAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

@IBAction func tapStampButton() {
    // Heart
    let heartAction = UIAlertAction(title: "Heart", style: .default) { _ in
        self.changeStampMode(stampName: "Heart")
    }
    // Star
    let starAction = UIAlertAction(title: "Star", style: .default) { _ in
        self.changeStampMode(stampName: "Star")
    }
    // Smile
    let smileAction = UIAlertAction(title: "Smile", style: .default) { _ in
        self.changeStampMode(stampName: "Smile")
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a stamp", message: nil, preferredStyle: .alert)
    alertController.addAction(heartAction)
    alertController.addAction(starAction)
    alertController.addAction(smileAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

private func changeStampMode(stampName: String) {
    sketchView.stampImage = UIImage(named: stampName)
    sketchView.drawTool = .stamp
}

@IBAction func tapFigureButton() {
    // Line
    let lineAction = UIAlertAction(title: "Line", style: .default) { _ in
        self.sketchView.drawTool = .line
    }
    // Arrow
    let arrowAction = UIAlertAction(title: "Arrow", style: .default) { _ in
        self.sketchView.drawTool = .arrow
    }
    // Rect
    let rectAction = UIAlertAction(title: "Rect", style: .default) { _ in
        self.sketchView.drawTool = .rectangleStroke
    }
    // Rectfill
    let rectFillAction = UIAlertAction(title: "Rect(Fill)", style: .default) { _ in
        self.sketchView.drawTool = .rectangleFill
    }
    // Ellipse
    let ellipseAction = UIAlertAction(title: "Ellipse", style: .default) { _ in
        self.sketchView.drawTool = .ellipseStroke
    }
    // EllipseFill
    let ellipseFillAction = UIAlertAction(title: "Ellipse(Fill)", style: .default) { _ in
        self.sketchView.drawTool = .ellipseFill
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a figure", message: nil, preferredStyle: .alert)
    alertController.addAction(lineAction)
    alertController.addAction(arrowAction)
    alertController.addAction(rectAction)
    alertController.addAction(rectFillAction)
    alertController.addAction(ellipseAction)
    alertController.addAction(ellipseFillAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

@IBAction func tapFilterButton() {
    // Normal
    let normalAction = UIAlertAction(title: "Normal", style: .default) { _ in
        self.sketchView.drawingPenType = .normal
    }
    // Blur
    let blurAction = UIAlertAction(title: "Blur", style: .default) { _ in
        self.sketchView.drawingPenType = .blur
    }
    // Neon
    let neonAction = UIAlertAction(title: "Neon", style: .default) { _ in
        self.sketchView.drawingPenType = .neon
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a filter type", message: nil, preferredStyle: .alert)
    alertController.addAction(normalAction)
    alertController.addAction(blurAction)
    alertController.addAction(neonAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

@IBAction func tapCameraButton() {
    // Camera
    let cameraAction = UIAlertAction(title: "Camera", style: .default) { _ in
        self.setImageFromCamera()
    }
    // Gallery
    let galleryAction = UIAlertAction(title: "Gallery", style: .default) { _ in
        self.setImageFromGallery()
    }
    // Cancel
    let cancelAction = UIAlertAction(title: "Cancel", style: .default) { _ in }

    let alertController = UIAlertController(title: "Please select a Picture", message: nil, preferredStyle: .alert)
    alertController.addAction(cameraAction)
    alertController.addAction(galleryAction)
    alertController.addAction(cancelAction)

    present(alertController, animated: true, completion: nil)
}

private func setImageFromCamera() {
    PhotoRequestManager.requestPhotoFromCamera(self){ [weak self] result in
        switch result {
        case .success(let image):
            self?.sketchView.loadImage(image: image)
        case .faild:
            print("failed")
        case .cancel:
            break
        }
    }
}

private func setImageFromGallery() {
    PhotoRequestManager.requestPhotoLibrary(self){ [weak self] result in
        switch result {
        case .success(let image):
            self?.sketchView.loadImage(image: image)
        case .faild:
            print("failed")
        case .cancel:
            break
        }
    }
}
 private func checkRecordingAvailability() {
//        let isScreenRecordingAvailable = RPScreenRecorder.shared().isAvailable
//        broadcastButton.isHidden = !isScreenRecordingAvailable
//        conferenceButton?.isHidden = !isScreenRecordingAvailable
//        infoLabel?.text = isScreenRecordingAvailable ? ViewController.kRecordingAvailableInfo : ViewController.kRecordingNotAvailableInfo
    }

} extension VideoCallViewController: RPScreenRecorderDelegate { func screenRecorderDidChangeAvailability(_ screenRecorder: RPScreenRecorder) { if Thread.isMainThread { // Assume we will get an error raised if we are actively broadcasting / capturing and access is "stolen". if (screenTrack == nil) { checkRecordingAvailability() } } else { DispatchQueue.main.async { self.screenRecorderDidChangeAvailability(screenRecorder) } } } } `

Expected Behavior

ear to view remote screen video but no luck, no sound or video

Actual Behavior

conectaron to the room is made but no video or audio, in console this logs from delegate.

Reproduces How Often

always, tried to change let recorder = RPScreenRecorder.shared() recorder.isMicrophoneEnabled = true to try audio but even no make a difference, some moments it was seen audio and video, but the method were wrong

Logs

the room is: Optional(<<TVIRoom: 0x283224b60> name: room_2098, state: Connecting, sid: , delegate: <Teachers.PruebaViewController: 0x109918c00>>)
Local identity lizziechiang@gmail.com
Number of connected Participants 1
Subscribed to eEc7069fEcB1eeefb9C2a6E3Bee87F48 audio track for Participant profesor@ios.com
Subscribed to Screen video track for Participant profesor@ios.com
2020-03-20 19:29:07.698619-0400 Teachers[1185:297203] CAMetalLayer ignoring invalid setDrawableSize width=1920.000000 height=0.000000

Versions

Voice iOS SDK

[ 3.1.0 via CocoaPods]

Xcode

[11.3.1]

iOS Version

[13.3.1]

iOS Device

[e.g. iPhone 7 Plus, iPad Pro]

piyushtank commented 4 years ago

Closing the issue as it is tracked here. Feel free to reopen if you think otherwise.

kjoe07 commented 4 years ago

the no data is about a vpn to make the connection. or a least a vpn provider using siphon vpn in iOS 13.3.1 and 13.4 so is not further help needed with this, thanks