Closed hyunoosung closed 3 years ago
To start a video call, you should create LocalVideoStream
beforehand and set it inside StartCallOptions
that you are passing to start the call. So the code would look something like following -
func startCall() {
if AVAudioSession.sharedInstance().recordPermission == .granted && AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
self.callClient!.getDeviceManager { (deviceManager, error) in
if error != nil {
print("Failed to get device manager")
} else {
self.deviceManager = deviceManager
print("Got device manager")
let firstCamera: VideoDeviceInfo? = self.deviceManager?.getCameraList()![0]
let localVideoStream = LocalVideoStream(camera: firstCamera)
localRenderer = try Renderer(localVideoStream: localVideoStream!)
localRendererView = VideoStreamView(view: (try? localRenderer?.createView())!)
let callees:[CommunicationUserIdentifier] = [CommunicationUserIdentifier(identifier: self.callee)]
print("Calling \(String(describing: callees))")
let options = StartCallOptions()!
options.videoOptions = VideoOptions(localVideoStream: self.localVideoStream!)!
self.call = self.callAgent?.call(participants: callees, options: options)
self.call?.delegate = self
}
//self.startLocalVideo()
}
} else {
// Ask permissions
self.requestRecordPermission()
self.requestDevicePermission()
}
}
Thank you so much for your help I could finish my code But I encountered some weird situations call.remoteParticipants on onCallStateChanged event doesn't include remoteStream sometimes. also local video doesn't render occasionally
i'm pasting my viewModel here
please have a look and tell me which causes problem
Thanks in advance!
func onCallStateChanged(_ call: Call!, args: PropertyChangedEventArgs!) {
print("\n----------------------------------")
print("onCallStateChanged: \(String(reflecting: call.state.name))")
print("----------------------------------\n")
callState = call.state
if callState == .connected {
print("remoteParticipants count: \(String(describing: self.call?.remoteParticipants.count))")
self.remoteParticipants = call?.remoteParticipants
call?.remoteParticipants.forEach { (remoteParticipant) in
if remoteParticipant.identity is CommunicationUserIdentifier {
let remoteParticipantIdentity = remoteParticipant.identity as! CommunicationUserIdentifier
let remoteParticipantIdentifier = remoteParticipantIdentity.identifier
print("RemoteParticipant identifier: \(String(describing: remoteParticipantIdentifier))")
print("RemoteParticipant displayName \(String(describing: remoteParticipant.displayName))")
print("\nRemoteVideoStream count for \(String(describing: remoteParticipant.displayName)): \(remoteParticipant.videoStreams.count)")
if !remoteParticipant.videoStreams.isEmpty {
print("\nBinding remoteVideoStream for \(String(describing: remoteParticipant.displayName))")
do {
if let remoteVideoStreams = remoteParticipant.videoStreams {
if remoteVideoStreams.count > 0 {
self.remoteRenderer = try Renderer(remoteVideoStream: remoteVideoStreams[0])
self.remoteRendererView = VideoStreamView(view: (try? self.remoteRenderer!.createView())!)
}
}
} catch {
print("Failed starting remoteVideoStream for \(String(describing: remoteParticipant.displayName)) : \(error.localizedDescription)")
}
} else {
print("RemoteVideoStream for \(String(describing: remoteParticipant.displayName)) not found.")
}
}
}
}
if callState == .disconnected || callState == .none {
remoteRenderer?.dispose()
localRenderer?.dispose()
call?.stopVideo(stream: localVideoStream!) { error in
if error != nil {
print("Failed to stop localVideoStream.")
} else {
print("LocalVideoStream successfully stoped.")
}
}
}
}
func onRemoteParticipantsUpdated(_ call: Call!, args: ParticipantsUpdatedEventArgs!) {
print("\n---------------------------")
print("onRemoteParticipantsUpdated")
print("---------------------------\n")
var callerIdentifier: String?
if call.callerId is CommunicationUserIdentifier {
let callerUserIdentifier = call.callerId as! CommunicationUserIdentifier
callerIdentifier = callerUserIdentifier.identifier
print("Caller identifier: \(String(describing: callerIdentifier))")
}
if let addedParticipants = args.addedParticipants {
if !addedParticipants.isEmpty {
print("addedParticipants: \(String(describing: args.addedParticipants.count))")
if callerIdentifier != nil {
print("callerIdentifier: \(String(describing: callerIdentifier))")
if let callerRemoteParticipant = args.addedParticipants.first(where: {($0.identity as! CommunicationUserIdentifier).identifier == callerIdentifier}) {
callerDisplayName = callerRemoteParticipant.displayName ?? "No displayName"
print("Incoming callerDisplayName: \(String(describing: callerDisplayName))")
}
}
}
}
if let removedParticipants = args.removedParticipants {
if !removedParticipants.isEmpty {
print("removedParticipants: \(String(describing: args.removedParticipants.count))")
if callerIdentifier != nil {
print("callerIdentifier: \(String(describing: callerIdentifier))")
if let callerRemoteParticipant = args.removedParticipants.first(where: {($0.identity as! CommunicationUserIdentifier).identifier == callerIdentifier}) {
let callerDisplayName = callerRemoteParticipant.displayName ?? "No displayName"
print("Removed callerDisplayName: \(String(describing: callerDisplayName))")
}
}
}
}
}
func onParticipantStateChanged(_ remoteParticipant: RemoteParticipant!, args: PropertyChangedEventArgs!) {
print("onParticipantStateChanged: \(String(describing: remoteParticipant)) - \(String(describing: args))")
}
func startCall() {
if AVAudioSession.sharedInstance().recordPermission == .granted && AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
let startCallOptions = StartCallOptions()
if let localVideoStream = self.localVideoStream {
do {
self.localRenderer = try Renderer(localVideoStream: localVideoStream)
self.localRendererView = VideoStreamView(view: (try self.localRenderer!.createView()))
} catch {
print("Failed binding localVideoRenderView")
}
let videoOptions = VideoOptions(localVideoStream: localVideoStream)
startCallOptions?.videoOptions = videoOptions
let callees:[CommunicationUserIdentifier] = [CommunicationUserIdentifier(identifier: self.callee)]
self.call = self.callAgent?.call(participants: callees, options: startCallOptions)
self.call?.delegate = self
self.call?.startVideo(stream: localVideoStream) { error in
if (error != nil) {
print("LocalVideoStream failed to start")
}
else {
print("LocalVideoStream started successfully")
}
}
}
} else {
// Ask permissions
self.requestRecordPermission()
self.requestDevicePermission()
print("RecordPermissiong or DevicePermission not granted. \n Please try again.")
}
}
func endCall() {
if let call = call {
call.hangup(options: HangupOptions(), completionHandler: { (error) in
if error != nil {
print("ERROR: It was not possible to hangup the call.")
} else {
}
print("Call ended")
})
}
}
func acceptCall() {
if let incomingCall = call {
print("Accept call")
let acceptCallOptions = AcceptCallOptions()
if let localVideoStream = self.localVideoStream {
do {
self.localRenderer = try Renderer(localVideoStream: localVideoStream)
self.localRendererView = VideoStreamView(view: (try self.localRenderer!.createView()))
} catch {
print("Failed binding localVideoRenderView")
}
acceptCallOptions!.videoOptions = VideoOptions(localVideoStream: localVideoStream)
incomingCall.startVideo(stream: localVideoStream) { error in
if (error != nil) {
print("LocalVideoStream failed to start")
}
else {
print("LocalVideoStream started successfully")
}
}
}
incomingCall.accept(options: acceptCallOptions!) { (error) in
if error != nil {
print("Failed to accpet incoming call.")
} else {
print("Incoming call accepted with acceptCallOptions.")
}
}
}
}
@hyounoo suggested fix should work in your case so I'm closing the issue. Please feel free to re-open if you encounter any issues.
should I create a new ticket?
We both ended up posting at the same time. I've reopened the ticket and looking at your posted code now.
call.remoteParticipants on onCallStateChanged event doesn't include remoteStream sometimes.
Participant videos may take some time to be added under the RemoteParticipant.videoStreams
collection. You should add a delegate to RemoteParticipant
objects to receive the RemoteParticipant.onVideoStreamsUpdated
event which gets fired when remote user stops/starts their video.
Please keep in mind that since there is a delay between SDK raising participant added event and you attaching delegate to that participant, you need to do both -
RemoteParticipant.videoStreams
when you are attaching that delegate, you should handle them explicitly as you've missed event for those videos.also local video doesn't render occasionally
This is unexpected. Can you please follow this guide and share log files for a repro so that we can investigate? Please also note down the time frame when it failed to render.
thanks for the help Really appreciate I will follow the guide and post the log tmr Been working straight two days in a row Now im off to bed
Finally, I've managed both localVideoStream and remoteVideoStream rendering working. onRemoteParticipants' StreamUpdated delegate event never occurs for me. So I had to give a 1 ~ 1.5 sec delay on main thread after call accepted or connected No idea how could solve this random remote stream event but with above trick.
Thanks for all the help so far!
Are you querying for remote video streams when handling participant added event?
Please keep in mind that since there is a delay between SDK raising participant added event and you attaching delegate to that participant, you need to do both -
- Add a delegate to handle video streams added/removed events that happen in the future, and
- If there are any videos available under RemoteParticipant.videoStreams when you are attaching that delegate, you should handle them explicitly as you've missed event for those videos.
It has to be done in this order only - attach event handler and then query for available video streams.
Are you querying for remote video streams when handling participant added event?
Please keep in mind that since there is a delay between SDK raising participant added event and you attaching delegate to that participant, you need to do both -
- Add a delegate to handle video streams added/removed events that happen in the future, and
- If there are any videos available under RemoteParticipant.videoStreams when you are attaching that delegate, you should handle them explicitly as you've missed event for those videos.
It has to be done in this order only - attach event handler and then query for available video streams.
Thanks, I followed your guide and code works with delegate now. Closing this issue now.
When receiving call, my local video view is working but it doesn't work on starting a new call.
Below is my code for starting a call
and the log is as below
Did I do something wrong?