cordova-rtc / cordova-plugin-iosrtc

Cordova iOS plugin exposing the WebRTC W3C API
MIT License
690 stars 338 forks source link

track.stop() does not stop stream transmission #140

Closed csturiale closed 5 years ago

csturiale commented 8 years ago

During a call/video call stopping the video stream does nothing and the remote peer follows to hear me.Stream transmission is not stopped.

ibc commented 8 years ago

Confirmed that by getting a local video track from a RTCPeerConnection and calling enabled=false or stop()` on it does nothing...

ibc commented 8 years ago

I've open a detailed issue in the WebRTC project. There is little I can do (unless there is a bug in my code):

https://bugs.chromium.org/p/webrtc/issues/detail?id=5784

ibc commented 8 years ago

Google WebRTC developers will ignore my bug report because I don't "reproduce it with AppRTCDemo". So if somebody can reproduce the issue in the f**king AppRTCDemo please report into the issue. I won't do it.

mark-veenstra commented 8 years ago

@ibc We think that we should set the source state to ended. In the libWebRTC code it is found here: https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/webrtc/api/videotracksource.cc#24 Is it possible from the JS side to call this setState and set it to ended? This should clean up the tracks and streams and set it all to ended to our opinion.

ibc commented 8 years ago

What about if the same source feeds two tracks (for example once clone() is implemented)? Calling stop() on a track should NOT also close the cloned track.

mark-veenstra commented 8 years ago

Hmm.. Indeed if you clone() a track and you just want to stop one track you would have an issue with this implementation.

RobAWarner commented 8 years ago

Is there any way at all to end/clear the tracks/streams? I won't be using clone() so that part isn't an issue for me. I noticed in PluginMediaStreamTrack.swift that is says // NOTE: There is no setState() anymore. Does this mean that there is no way to clear them? I also noticed that after a call, CPU usage is still rather high (~80%), I assume this is because the tracks are still there?

ibc commented 8 years ago

@MonsterKiller please read this full history. Obviously there is no a easy solution for this problem. Otherwise the issue would no longer exist.

RobAWarner commented 8 years ago

@ibc Hey, I have read this issue and it was mentioned by @mark-veenstra that you may be able to set the source state to ended, but this would affect the use of clone(), I was enquiring as to if this could still be used for people who don't need to use clone() (who could modify the plugin locally) or whether this functionality does not work for ending the stream at all. Thanks.

ibc commented 8 years ago

Got a response with good rationale in the chromium tracker:

Can you summarize what the issue you're still seeing is? setState() isn't intended to be a public API, but setEnabled(false) should definitely cause muted (black) video to be sent. Is that not happening?

Or is the issue that the source is still alive after calling setEnabled(false)? That seems expected since you could call setEnabled(true) later. If you remove all references to the track, the tracks (along with the source) should be destroyed. Is that not working, or is it not sufficient?

Ok, currently MediaStreamTrack.stop() does the following (in Swift land):

func stop() {
    NSLog("PluginMediaStreamTrack#stop() [kind:%@, id:%@]", String(self.kind), String(self.id))

    self.rtcMediaStreamTrack.setEnabled(false)
}

self.rtcMediaStreamTrack is the native Obj-C object.

I cannot check this for long time, so I hope someone can answer the following questions regarding what currently happens when calling track.stop() with this plugin:

If the answer is yes, then the Swift stop() method above should also set self.rtcMediaStreamTrack = nil (or similar) and fire a close event. Or may be all the Swift PluginMediaStream instances holding this track should remove it from their map of tracks, so this track is garbage collected (and also the native Obj-C track object).

I hope I'm right and also hope that someone can work on it and send a PR if interested.

mark-veenstra commented 8 years ago

We just did some tests and what we see is that the PluginMediaStream and PluginMediaStreamTrack of the PluginGetUserMedia are not released after closing all PeerConnections and closing all tracks etc.

When I do a dump() when having a call between 2 devices, it displays:

2016-11-04 13:52:11.483585 MyCallApp[1369:1183872] iosrtcPlugin#dump()
2016-11-04 13:52:11.483718 MyCallApp[1369:1183872] - PluginRTCPeerConnection [id:26273]
2016-11-04 13:52:11.483786 MyCallApp[1369:1183872] - PluginRTCPeerConnection [id:28582]
2016-11-04 13:52:11.484176 MyCallApp[1369:1183872] - PluginMediaStream [97659D53-388F-4533-8F2D-3ED39CADC339:A=1:V=1]
2016-11-04 13:52:11.484388 MyCallApp[1369:1183872] - PluginMediaStream [default:A=1:V=1]
2016-11-04 13:52:11.484475 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:23B1E761-CA00-44D6-82DD-90A2E440B44E, kind:video]
2016-11-04 13:52:11.484610 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:F03A4D41-6812-406F-B213-3514FD9AC2B1, kind:audio]
2016-11-04 13:52:11.484688 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:dsKLOozW, kind:video]
2016-11-04 13:52:11.484802 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:vXyFpbIU, kind:audio]
2016-11-04 13:52:11.484922 MyCallApp[1369:1183872] - PluginMediaStreamRenderer [id:52925]
2016-11-04 13:52:11.485016 MyCallApp[1369:1183872] - PluginMediaStreamRenderer [id:88121]

After I hangup the call only the following tracks and streams are still there:

2016-11-04 13:52:47.597683 MyCallApp[1369:1183872] iosrtcPlugin#dump()
2016-11-04 13:52:47.598276 MyCallApp[1369:1183872] - PluginMediaStream [97659D53-388F-4533-8F2D-3ED39CADC339:A=1:V=1]
2016-11-04 13:52:47.598577 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:23B1E761-CA00-44D6-82DD-90A2E440B44E, kind:video]
2016-11-04 13:52:47.598869 MyCallApp[1369:1183872] - PluginMediaStreamTrack [id:F03A4D41-6812-406F-B213-3514FD9AC2B1, kind:audio]

These seem to be related to the created getUserMedia labels here: https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L83 https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L164 https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L174

So eacht new call I start, will do a new call to the getUserMedia and recreate a stream and audio/video track for getUserMedia. So after each call all connections and all remote streams/tracks are removed. But the getUserMedia remain.

Maybe the main question is why are they not removed even when the rtcPeerConnection is removed and all other references? I am not really into Swift so I can't answer.

I did try to reuse the same ids for the getUserMedia so they won't create new ones, like this:

Changed https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L6 to:

var rtcPeerConnectionFactory: RTCPeerConnectionFactory
var getUserMediaId : String = NSUUID().UUIDString
var getUserMediaAudioId : String = NSUUID().UUIDString
var getUserMediaVideoId : String = NSUUID().UUIDString

Changed https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L83 to:

rtcMediaStream = self.rtcPeerConnectionFactory.mediaStreamWithLabel(getUserMediaId)

Changed https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L164 to:

rtcVideoTrack = self.rtcPeerConnectionFactory.videoTrackWithID(getUserMediaVideoId,

Changed https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/src/PluginGetUserMedia.swift#L174 to:

rtcAudioTrack = self.rtcPeerConnectionFactory.audioTrackWithID(getUserMediaAudioId)

With these changes the same tracks are used and I am not getting new tracks after each getUserMedia. But the stream is still duplicated.

ibc commented 8 years ago

So eacht new call I start, will do a new call to the getUserMedia and recreate a stream and audio/video track for getUserMedia. So after each call all connections and all remote streams/tracks are removed. But the getUserMedia remain.

Maybe the main question is why are they not removed even when the rtcPeerConnection is removed and all other references? I am not really into Swift so I can't answer.

The fact that you close a PeerConnection does not mean, at all, that you close the local MediaStream. This is true even in a browser.

Local streams must be closed by the user, and here is where we are, because calling track.stop() does not currently work.

However, what I need to know is a response to my previous question:

Does it cause muted audio or black video? or absolutely nothing changes and the remote peer still can hear/see us?

RobAWarner commented 8 years ago

Does it cause muted audio or black video? or absolutely nothing changes and the remote peer still can hear/see us?

I had a quick go with this today with a call between an iPhone and a web browser version. If I run the following on the iPhone using this plugin, during a call:

localStream.getAudioTracks().forEach(function(track) { track.stop(); }); localStream.getVideoTracks().forEach(function(track) { track.stop(); });

The remote party does see black/blank video and no longer hears the audio.

ibc commented 8 years ago

@MonsterKiller good to know, thanks.

logidelic commented 7 years ago

I've read the entire thread, and I apologize if the answer is "obviously not", but just to be sure:

Barring a proper fix for this, is it possible for us to somehow completely re-initialize iosrtc/libwebrtc when we need to bring things back to a clean state (free resources, etc)?

FWIW: I've found that, if I've attached a local stream to an HTML video element (using el.src = window.URL.createObjectURL(stream)), then I need to ensure that said element gets deleted (i.e. detached from my document body) before I can successfully open up a new webrtc stream using getUserMedia... Even if the old stream was already "stopped" as per the above discussion.

saghul commented 7 years ago

Barring a proper fix for this, is it possible for us to somehow completely re-initialize iosrtc/libwebrtc when we need to bring things back to a clean state (free resources, etc)?

Not that I know of.

FWIW: I've found that, if I've attached a local stream to an HTML video element (using el.src = window.URL.createObjectURL(stream)), then I need to ensure that said element gets deleted (i.e. detached from my document body) before I can successfully open up a new webrtc stream using getUserMedia... Even if the old stream was already "stopped" as per the above discussion.

Hum, that's extrange. At any rate, doesn't el.src = ''; work for you?

undersieg commented 7 years ago

I'm new in swift but, I used capture session to remove all inputs and outputs and it work for me.

dispatch_async(self.queue) { [weak pluginMediaStreamTrack] in 
pluginMediaStreamTrack?.stop()
              if self.m_captureSession != nil {
                   for input in self.m_captureSession.inputs{
                    self.m_captureSession.removeInput(input as! AVCaptureInput);
                }
                for output in self.m_captureSession.outputs{
                    self.m_captureSession.removeOutput(output as! AVCaptureOutput);
                }
                self.m_captureSession.stopRunning();
                self.m_captureSession = nil;
            }

}
dmarcs commented 7 years ago

@ibc @saghul Why don't you just create a patch that adds the setState method again?

- (BOOL)setState:(RTCTrackState)state { 
    return self.mediaTrack->set_state( 
        [RTCEnumConverter convertTrackStateToNative:state]); 
}
saghul commented 7 years ago

@dmarcus1 what is that supposed to accomplish?

dmarcs commented 7 years ago

@saghul Take a look at https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/blob/1c543747697d24691c58ed3e6f82484fc2600dd2/src/PluginMediaStreamTrack.swift#L84

Right now the MediaStreamTrack stop function is as follows:

// TODO: No way to stop the track.
// Check https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140
func stop() {
   NSLog("PluginMediaStreamTrack#stop() [kind:%@, id:%@]", String(self.kind), String(self.id))
    
   NSLog("PluginMediaStreamTrack#stop() | stop() not implemented (see: https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140")
    
   // NOTE: There is no setState() anymore
   // self.rtcMediaStreamTrack.setState(RTCTrackStateEnded)
    
   // Let's try setEnabled(false), but it also fails.
   self.rtcMediaStreamTrack.setEnabled(false)
}

When you call MediaStreamTrack.stop() right now, it only makes the track not enabled. The track can be enabled in the future, so setEnabled is meant to be toggled. That's what a WebRTC employee explained to Iñaki: https://bugs.chromium.org/p/webrtc/issues/detail?id=5784. Here's the relevant response:

Can you summarize what the issue you're still seeing is? setState isn't intended to be a public API, but setEnabled(false) should definitely cause muted (black) video to be sent. Is that not happening?

Or is the issue that the source is still alive after calling setEnabled(false)? That seems expected since you could call setEnabled(true) later. If you remove all references to the track, the tracks (along with the source) should be destroyed. Is that not working, or is it not sufficient?

He's saying that you can no longer call setState in libwebrtc. Without setState, it's not possible to change the state of the track. setEnabled(false) is not a good solution because it does not stop the MediaStreamTrack. If you modify the libwebrtc library by adding a setState function, then you can call setState(RTCTrackStateEnded) and this bug will be solved.

Earlier in this thread, @ibc suggested self.rtcMediaStreamTrack = nil. The WebRTC employee says that this is how you destroy the track properly. Aside from creating a patch with the setState function, this might be the best solution as it frees the camera.

As the code stands right now there is a huge problem because once you start a MediaStreamTrack, there's no way to destroy it. That's a problem because the camera never "lets go" of the tracks. This is likely to be the cause of #298. I was able to reproduce that bug and fix it by calling self.rtcMediaStreamTrack = nil. I made a function called freeCamera which sets all MediaStreamTracks and MediaStreams to nil, effectively destroying them. Let me know if you want me to make a PR for this. The downside to this is that iosRTC is supposed to be identical to WebRTC on the computer. Obviously there is no freeCamera function in WebRTC. Calling stop on a MediaStreamTrack on the computer does not remove the MediaStreamTrack, so it shouldn't in iosRTC.

ibc commented 7 years ago

Calling stop on a MediaStreamTrack on the computer does not remove the MediaStreamTrack, so it shouldn't in iosRTC.

But calling track.stop() on the plugin may set to nil the native rtcMediaStreamTrack (regardless the associated JS MediaStreamTrack still exists). A PR doing that would make more sense IMHO.

dmarcs commented 7 years ago

Interesting idea. I didn't think of that, but it makes a lot of sense.

vertazzar commented 6 years ago

@dmarcs can you provide the code that dereferences the tracks? i would like to fork it and make it work...

dmarcs commented 6 years ago

@vertazzar I'm guessing you ran into the same issue I ran into where there's a problem with switching from the front to back camera after turning off the phone. As iosRTC stands right now, there's no way to use it in production because of that bug. I got it to work, but in a very hacky way. Iñaki probably won't accept it if you make a pull request because I added the method freeCamera which isn't a WebRTC method, but here it is:

func freeCamera(_ command: CDVInvokedUrlCommand) {
    NSLog("iosrtcPlugin#freeCamera()")

    var localId:String?
    var pcId:Int?
    for (id, _) in self.pluginRTCPeerConnections {
        localId = String(describing: self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams[0])
        pcId = id
    }

    var localVideoId:String?
    var localAudioId:String?
    for (id, pluginMediaStream) in self.pluginMediaStreams {
        NSLog("- PluginMediaStream %@", String(pluginMediaStream.rtcMediaStream.description))
        if(String(pluginMediaStream.rtcMediaStream.description) == localId) {
            localVideoId = self.pluginMediaStreams[id]!.videoTracks.values.first?.id
            localAudioId = self.pluginMediaStreams[id]!.audioTracks.values.first?.id
            localId = id
        }
    }

    if(self.pluginMediaStreams.count < 1 || self.pluginMediaStreamTracks.count < 1 || self.pluginMediaStreams.count < 1) {
        return;
    }

    if let yourStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
        if let yourAudioTrack = self.pluginMediaStreamTracks[localAudioId!]?.rtcMediaStreamTrack {
            yourStream.removeAudioTrack(yourAudioTrack as! RTCAudioTrack)
        }
        if let yourVideoTrack = self.pluginMediaStreamTracks[localVideoId!]?.rtcMediaStreamTrack {
            yourStream.removeVideoTrack(yourVideoTrack as! RTCVideoTrack)
        }
    }
    if let yourPC = self.pluginRTCPeerConnections[pcId!]?.rtcPeerConnection {
        if let yourMediaStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
            yourPC.remove(yourMediaStream as! RTCMediaStream)
        }
    }
}

I wrote this code a while ago, so I can't remember why I didn't set variables equal to nil, but I think doing that caused more issues even though it's the correct way to dereference.

I was hoping that by now Safari would support WebRTC, which it does on mobile Safari, but not in UIWebView or WKWebView.

vertazzar commented 6 years ago

@dmarcs thank you very much for the code, I will test it out tonight

generally I don't see the problem of adding a non-standard way of freeing the resources since the entire purpose of the plugin is to provide WebRTC for ios UIWebView. Writing some wrapper with few if-s like isCordovaIOS() is no big deal for the case where the bug cannot be fixed according to the standard RTC implementation

dmarcs commented 6 years ago

@vertazzar Okay sounds good. Let me know if it works. If not you can try setting some variables above equal to nil which will definitely free your camera, but potentially cause other issues.

vertazzar commented 6 years ago

@dmarcs I have tested it and it seems that it's working without any problems. Even if I drop the call, and re-initiate the call

dmarcs commented 6 years ago

@vertazzar That's good to hear 😃 Someone using my Cordova CallKit plugin also used it, and it worked.

fuub commented 6 years ago

I'm having this same issue, I've added @dmarcs freecamera method in iosrtcPlugin.swift, how do I call it in javascript to release the camera?

dmarcs commented 6 years ago

@fuub You need to add this function to iosrtc.js. It allow you to call the swift function from javascript:

function freeCamera() {
    exec(null, null, 'iosrtcPlugin', 'freeCamera', []);
}

Hope this helps.

fuub commented 6 years ago

Hello David,

Thanks for the quick response!

I was able to call the freeCamera function from javascript now, but after calling it after the call is ended it is giving me the error on the image below. Is this anything you know the cause?

[image: Inline image 1]

On Thu, Dec 28, 2017 at 12:24 AM, David Marcus notifications@github.com wrote:

@fuub https://github.com/fuub You need to add this function to iosrtc.js https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/blob/d38fe4dabb6d433b20ec6fe4efd65fa86aa710a4/js/iosrtc.js. It allow you to call the swift function from javascript:

function freeCamera() { exec(null, null, 'iosrtcPlugin', 'freeCamera', []); }

Hope this helps.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140#issuecomment-354202624, or mute the thread https://github.com/notifications/unsubscribe-auth/ARu1DfPNLgHev7aIQjlCc6Q3EZBlONRlks5tEt_VgaJpZM4Hq0Xq .

dmarcs commented 6 years ago

@fuub Sorry but I can't see the image.

fuub commented 6 years ago

I solved by calling the freecamera sooner and now it frees the camera.

but still didn't fixed my main issue, which is after the first call the local video doesn't show up, or show up just minutes later of the ongoing call, totally random. In the first call it always shows up perfectly.

Although it doesn't show up the local video, the video is being sent and working fine, maybe it is some problem with rendering I dont know.

Have you experienced something similar?

Sorry for asking this is just there really isn't much documentation to search for this problems..

On Fri, Dec 29, 2017 at 11:32 PM, David Marcus notifications@github.com wrote:

@fuub https://github.com/fuub Sorry but I can't see the image.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140#issuecomment-354512692, or mute the thread https://github.com/notifications/unsubscribe-auth/ARu1DaCgo-_viAHnd2ZMbZaSmH9UKdrLks5tFXaZgaJpZM4Hq0Xq .

dmarcs commented 6 years ago

@fuub I also had the problem of not being able to see the local video (The first call always worked perfectly for me too). See #298 for more details. The freeCamera method fixed that problem for me, but I only called it at the end of a call. I'm not sure why you have to call it earlier. That might be part of your problem. I would use xCode to analyze the webRTC variables, and see if you can get to the bottom of the issue that way.

fuub commented 6 years ago

Ok I will try to check webRTC variables, btw the previous image and the error it gives when calling freecamera after call ending is this: https://imgur.com/a/b4M9s

On Thu, Jan 4, 2018 at 1:37 AM, David Marcus notifications@github.com wrote:

@fuub https://github.com/fuub I also had the problem of not being able to see the local video (The first call always worked perfectly for me too). See #298 https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/298 for more details. The freeCamera method fixed that problem for me, but I only called it at the end of a call. I'm not sure why you have to call it earlier. That might be part of your problem. I would use xCode to analyze the webRTC variables, and see if you can get to the bottom of the issue that way.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140#issuecomment-355176700, or mute the thread https://github.com/notifications/unsubscribe-auth/ARu1Dcej1NgtvovOK5fW_hxDdzL8XP-Jks5tHCtwgaJpZM4Hq0Xq .

alkoschuster commented 6 years ago

We also implemented the freeCamera function in our fork and it works splendid. We did however had to make one change. The first for loop in the function assumes there is at least 1 localStream which, for us, was not the case and it made the app crash. A small if around it fixed that problem. The final code we implemented is as follows:

func freeCamera(_ command: CDVInvokedUrlCommand) {
        NSLog("iosrtcPlugin#freeCamera()")

        var localId:String?
        var pcId:Int?
        for (id, _) in self.pluginRTCPeerConnections {
            if self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams.count >= 1 {
                localId = String(describing: self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams[0])
                pcId = id
            }
        }

        var localVideoId:String?
        var localAudioId:String?
        for (id, pluginMediaStream) in self.pluginMediaStreams {
            NSLog("- PluginMediaStream %@", String(pluginMediaStream.rtcMediaStream.description))
            if(String(pluginMediaStream.rtcMediaStream.description) == localId) {
                localVideoId = self.pluginMediaStreams[id]!.videoTracks.values.first?.id
                localAudioId = self.pluginMediaStreams[id]!.audioTracks.values.first?.id
                localId = id
            }
        }

        if(self.pluginMediaStreams.count < 1 || self.pluginMediaStreamTracks.count < 1 || self.pluginMediaStreams.count < 1) {
            return;
        }

        if let yourStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
            if let yourAudioTrack = self.pluginMediaStreamTracks[localAudioId!]?.rtcMediaStreamTrack {
                yourStream.removeAudioTrack(yourAudioTrack as! RTCAudioTrack)
            }
            if let yourVideoTrack = self.pluginMediaStreamTracks[localVideoId!]?.rtcMediaStreamTrack {
                yourStream.removeVideoTrack(yourVideoTrack as! RTCVideoTrack)
            }
        }
        if let yourPC = self.pluginRTCPeerConnections[pcId!]?.rtcPeerConnection {
            if let yourMediaStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
                yourPC.remove(yourMediaStream as! RTCMediaStream)
            }
        }
    }
fuub commented 6 years ago

Hi Alko,

Did that change and now I get a similar error below: https://imgur.com/a/UJyQb

On Thu, Jan 4, 2018 at 1:54 PM, Alko notifications@github.com wrote:

We also implemented the freeCamera function in our fork and it works splendid. We did however had to make one change. The first for loop in the function assumes there is at least 1 localStream which, for us, was not the case and it made the app crash. A small if around it fixed that problem. The final code we implemented is as follows:

func freeCamera(_ command: CDVInvokedUrlCommand) { NSLog("iosrtcPlugin#freeCamera()")

    var localId:String?
    var pcId:Int?
    for (id, _) in self.pluginRTCPeerConnections {
        if self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams.count >= 1 {
            localId = String(describing: self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams[0])
            pcId = id
        }
    }

    var localVideoId:String?
    var localAudioId:String?
    for (id, pluginMediaStream) in self.pluginMediaStreams {
        NSLog("- PluginMediaStream %@", String(pluginMediaStream.rtcMediaStream.description))
        if(String(pluginMediaStream.rtcMediaStream.description) == localId) {
            localVideoId = self.pluginMediaStreams[id]!.videoTracks.values.first?.id
            localAudioId = self.pluginMediaStreams[id]!.audioTracks.values.first?.id
            localId = id
        }
    }

    if(self.pluginMediaStreams.count < 1 || self.pluginMediaStreamTracks.count < 1 || self.pluginMediaStreams.count < 1) {
        return;
    }

    if let yourStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
        if let yourAudioTrack = self.pluginMediaStreamTracks[localAudioId!]?.rtcMediaStreamTrack {
            yourStream.removeAudioTrack(yourAudioTrack as! RTCAudioTrack)
        }
        if let yourVideoTrack = self.pluginMediaStreamTracks[localVideoId!]?.rtcMediaStreamTrack {
            yourStream.removeVideoTrack(yourVideoTrack as! RTCVideoTrack)
        }
    }
    if let yourPC = self.pluginRTCPeerConnections[pcId!]?.rtcPeerConnection {
        if let yourMediaStream = self.pluginMediaStreams[localId!]?.rtcMediaStream {
            yourPC.remove(yourMediaStream as! RTCMediaStream)
        }
    }
}

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140#issuecomment-355287665, or mute the thread https://github.com/notifications/unsubscribe-auth/ARu1DX-4UNQWBXFyfA4VdvCMmjISecwEks5tHNgngaJpZM4Hq0Xq .

alkoschuster commented 6 years ago

@fuub Don't know why this is happening on your end. You can probably find out more if you look at the debug data available, but just by looking at the code, maybe changing it to this will fix it for you:

func freeCamera(_ command: CDVInvokedUrlCommand) {
        NSLog("iosrtcPlugin#freeCamera()")

        var localId:String?
        var pcId:Int?
        for (id, _) in self.pluginRTCPeerConnections {
            if self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams.count >= 1 {
                localId = String(describing: self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams[0])
                pcId = id
            }
        }

        if localId == nil {
            return;
        }

        var localVideoId:String?
        var localAudioId:String?
        var streamId:String?
        for (id, pluginMediaStream) in self.pluginMediaStreams {
            NSLog("- PluginMediaStream %@", String(pluginMediaStream.rtcMediaStream.description))
            if(String(pluginMediaStream.rtcMediaStream.description) == localId) {
                localVideoId = self.pluginMediaStreams[id]!.videoTracks.values.first?.id
                localAudioId = self.pluginMediaStreams[id]!.audioTracks.values.first?.id
                streamId = id
            }
        }

        if(self.pluginMediaStreams.count < 1 || self.pluginMediaStreamTracks.count < 1 || self.pluginMediaStreams.count < 1 || streamId == nil) {
            return;
        }

        if let yourStream = self.pluginMediaStreams[streamId!]?.rtcMediaStream {
            if let yourAudioTrack = self.pluginMediaStreamTracks[localAudioId!]?.rtcMediaStreamTrack {
                yourStream.removeAudioTrack(yourAudioTrack as! RTCAudioTrack)
            }
            if let yourVideoTrack = self.pluginMediaStreamTracks[localVideoId!]?.rtcMediaStreamTrack {
                yourStream.removeVideoTrack(yourVideoTrack as! RTCVideoTrack)
            }
        }
        if let yourPC = self.pluginRTCPeerConnections[pcId!]?.rtcPeerConnection {
            if let yourMediaStream = self.pluginMediaStreams[streamId!]?.rtcMediaStream {
                yourPC.remove(yourMediaStream as! RTCMediaStream)
            }
        }
    }
fuub commented 6 years ago

Thanks for the help Alko,

It doesn't give any error now with that code but also doesn't free camera :)

But I discovered something... if I use the code right before call ends it is releasing the camera, and if I background the app and then foreground it, it works again nicely. Only if it stays foreground on the second try the local video doesn't work... Maybe it is a different issue afterall...

On Fri, Jan 5, 2018 at 10:01 AM, Alko notifications@github.com wrote:

@fuub https://github.com/fuub Don't know why this is happening on your end. You can probably find out more if you look at the debug data available, but just by looking at the code, maybe changing it to this will fix it for you:

func freeCamera(_ command: CDVInvokedUrlCommand) { NSLog("iosrtcPlugin#freeCamera()")

    var localId:String?
    var pcId:Int?
    for (id, _) in self.pluginRTCPeerConnections {
        if self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams.count >= 1 {
            localId = String(describing: self.pluginRTCPeerConnections[id]!.rtcPeerConnection.localStreams[0])
            pcId = id
        }
    }

    if localId == nil {
        return;
    }

    var localVideoId:String?
    var localAudioId:String?
    var streamId:String?
    for (id, pluginMediaStream) in self.pluginMediaStreams {
        NSLog("- PluginMediaStream %@", String(pluginMediaStream.rtcMediaStream.description))
        if(String(pluginMediaStream.rtcMediaStream.description) == localId) {
            localVideoId = self.pluginMediaStreams[id]!.videoTracks.values.first?.id
            localAudioId = self.pluginMediaStreams[id]!.audioTracks.values.first?.id
            streamId = id
        }
    }

    if(self.pluginMediaStreams.count < 1 || self.pluginMediaStreamTracks.count < 1 || self.pluginMediaStreams.count < 1 || streamId == nil) {
        return;
    }

    if let yourStream = self.pluginMediaStreams[streamId!]?.rtcMediaStream {
        if let yourAudioTrack = self.pluginMediaStreamTracks[localAudioId!]?.rtcMediaStreamTrack {
            yourStream.removeAudioTrack(yourAudioTrack as! RTCAudioTrack)
        }
        if let yourVideoTrack = self.pluginMediaStreamTracks[localVideoId!]?.rtcMediaStreamTrack {
            yourStream.removeVideoTrack(yourVideoTrack as! RTCVideoTrack)
        }
    }
    if let yourPC = self.pluginRTCPeerConnections[pcId!]?.rtcPeerConnection {
        if let yourMediaStream = self.pluginMediaStreams[streamId!]?.rtcMediaStream {
            yourPC.remove(yourMediaStream as! RTCMediaStream)
        }
    }
}

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BasqueVoIPMafia/cordova-plugin-iosrtc/issues/140#issuecomment-355516808, or mute the thread https://github.com/notifications/unsubscribe-auth/ARu1DeshadPclY2nb84UET78ogxGr7ziks5tHfL6gaJpZM4Hq0Xq .

HugoHeneault commented 6 years ago

@saghul @ibc What do you think of what was done here by @alkoschuster ?

Right now my solution is force the user to kill and restart the app after the calls... :(

Bernard-Ip commented 6 years ago

@alkoschuster @dmarcs

I tried to use the freeCamera function on iosrtc plugin. I added the function to iosrtcPlugin.swift, and also added function: freeCamera() { exec(null, null, 'iosrtcPlugin', 'freeCamera', []); } to iosrtc.js

When calling cordova.plugins.iosrtc.freeCamera(), it said freeCamera function is undefined.

Could anyone point me to the right way of using the function? Thanks.

MatthewTrout commented 5 years ago

@Bernard-Ip if you're just doing as I was, modifying the content in the xcode project to test it out, if you do a cordova build you'll find that the contents of the plugin's .js will be replaced, so double check that your changes are still there..

hthetiot commented 5 years ago

related but not directly

hthetiot commented 5 years ago

What a fucking mess, I will consider implementing MediaStreamMediaStreamTrack Stop instead. freeCamera does not make sense in WebRTC land.

hthetiot commented 5 years ago

calling localStream.stop() or localStream.getTracks().forEach(function (track) { track.stop() }) or pc1.getSenders().forEach(function (track) { track.stop() }) work on 5.0.2+ and has been tested again on 5.0.4.

See used test scripts with localStream global used to call stop functions.

Mihai-github commented 2 years ago

Hi, I'm having an issue with stopping the tracks.

After I first call the track.stop() function when switching between camera the old track stops after few seconds, but if a try to switch again the function won't work and there's no error or something to check if I do something wrong

const streamRef = useRef<MediaStream | null>(null);

.....
..... 

if (streamRef.current) {
                    console.log('xxxx', streamRef.current.getVideoTracks());
                    streamRef.current
                        .getTracks()
                        .forEach((track: MediaStreamTrack) => {
                            track.stop();
                        });
                    streamRef.current = null;
}

.......
.......

streamRef.current = currentStream;
hthetiot commented 2 years ago

@Mihai-github due to the nature of this plugin webrtc async track stop, you may want to try to add a setTimeout , or wait for stream end event before adding the next stream. Alternatively using RTCSender remove track and removeTrack on stream may also improve the issue caused by async nature of the SHIM.