GetStream/stream-video-js

Video/audio tracks not properly closed when the stream is unmounted on iOS.

Closed this issue ยท 22 comments

Which package/packages do you use?

  • @stream-io/video-react-sdk
  • @stream-io/video-react-native-sdk
  • @stream-io/video-client

Describe the bug
After a stream is ended/unmounted, the video/audio tracks are still active. This can be observed by seeing that the indicator for camera/microphone on the phone is active (yellow/green indicator light). It seems like for a split second the tracks are really stopped since the indicator turns off, but after a second it seems like they become active again.

To Reproduce
Steps to reproduce the behavior:

  1. Open Sample app/Dogfooding app (but this happens in my own app as well)
  2. Start a livestream
  3. Leave the livestream
  4. Observe that the camera/microphone active indicators are still present, even after leaving the livestream.

Expected behavior
After leaving the livestream, the camera/microphone active indicators should immediately stop.

Screenshots
N/A

Version

  • @stream-io/video-react-native-sdk 0.3.7

React Native

  • OS: iOS
  • Device: real iPhone 14 Pro

Additional context
This issue does NOT appear on Android. It seems to only affect iOS.

I have found that I have success by using the useMicrophoneState and useCameraState hooks from useCallStateHooks and then disabling the camera/microphone from there. That's great, but shouldn't the call.microphone.disable() and call.camera.disable() work the same way? So, to reiterate, if I disable the camera/mic from the specific hooks, then it seems to work, but if I access it via the call object from useCall, then it doesn't seem to work.

Hey @fobos531, thanks for reporting the issue to us. We are looking into it, and we will get back to you soon. ๐Ÿ˜„

Hey @fobos531, thanks for reporting the issue to us. We are looking into it, and we will get back to you soon. ๐Ÿ˜„

Thank you! I've also received reports from my users that even with the useMicrophoneState and useCameraState hooks the microphone/camera are not always closed.

Hey @fobos531, for the viewer side of the livestream I believe you don't want the camera access at all, right?

Hey @fobos531, for the viewer I believe you don't want the camera access at all, right?

Yes, but I dont want the microphone either. I tried passing the prop for initial disabling of camera and mic to the stream video component - the camera was properly disabled right from the start, but the microphone was still being turned on even though the initial prop was set to off.

But for me the bigger issue is when I do not pass the initial prop, and then stop/end/leave the call. Thats when the mic and camera remain being accessed.

Yes, but I don't want the microphone either. I tried passing the prop for initial disabling of camera and mic to the stream video component - the camera was properly disabled right from the start, but the microphone was still being turned on even though the initial prop was set to off.

For a host, the Microphone is expected to be on(forever). From the Webrtc 118 version, when audio is muted, the track is not stopped/released every time on mute, but the track.enabled is toggled; hence, the mic will always be accessed. Through this mechanism, we achieve speaking while muted or related features. Google Meet and other applications have the same behaviour ๐Ÿ˜„

For Viewer, you can disable the camera and microphone by passing:

mediaDeviceInitialState={{
        initialAudioEnabled: false,
        initialVideoEnabled: false,
      }}

to StreamCall.

But for me the bigger issue is when I do not pass the initial prop, and then stop/end/leave the call. Thats when the mic and camera remain being accessed.

For this, please add after await call.leave().

      await camera.disable();
      await microphone.disable();

But for me the bigger issue is when I do not pass the initial prop, and then stop/end/leave the call. Thats when the mic and camera remain being accessed.

For this, please add after await call.leave().

      await camera.disable();
      await microphone.disable();

Hey @khushal87

Thank you for the tip. While this mostly works, I discovered it doesn't work 100% of the time. For example, I cold-start the app, start the call, end the call and both the camera and microphone are properly stopped. Then I start the call again and end it, and the camera is disabled, but microphone still runs. See screenshot (camera, recently means the camera was properly stopped, while the microphone appears to be running):
IMG_2659

Then I do that again, and then both are disabled. So it seems to work most of the time, with ocassional deviation from the pattern. Is there anything in GetStream's implementation that would cause this behavior?

But for me the bigger issue is when I do not pass the initial prop, and then stop/end/leave the call. Thats when the mic and camera remain being accessed.

For this, please add after await call.leave().

      await camera.disable();
      await microphone.disable();

Hey @khushal87

Thank you for the tip. While this mostly works, I discovered it doesn't work 100% of the time. For example, I cold-start the app, start the call, end the call and both the camera and microphone are properly stopped. Then I start the call again and end it, and the camera is disabled, but microphone still runs. See screenshot (camera, recently means the camera was properly stopped, while the microphone appears to be running): IMG_2659

Then I do that again, and then both are disabled. So it seems to work most of the time, with ocassional deviation from the pattern. Is there anything in GetStream's implementation that would cause this behavior?

Hey @fobos531, we will give it a look for possibilities that might cause this bug. Thanks for reporting ๐Ÿ˜„

@santhoshvai I still encounter this on version 0.6.11, when the video call hangs up the green light (camera) stays on.

Sometimes instead of the RingingCallContent being unmounted on hung up I see just the background on both iOS and Android with no way to close this screen unless I kill the app.

I believe there's a race condition somewhere or there's an error (thus exiting early) preventing the screen to unmount properly.

this also happens on dogfood example @oliverlaz ^^

device: iPhone 15 Pro Max on iOS 17.4.1

video demo of dogfood:

VID_20240425_145540_720.mp4

hi @efstathiosntonas, thanks for providing your input. We'll check this and get back to you.
cc: @khushal87

Hey @efstathiosntonas, do you see this issue everytime on our DF app or it occurs sometimes?

@khushal87

steps:

  1. Open apps, no fast refresh
  2. made 10 calls from Android device and hangup on iOS
  3. The green light stayed on. It turns off for a fraction of the second and then turns on again

repeated steps 2 and 3 but I answered the call on iOS, same thing happens.

Did it the other way around, iOS called Android, same thing.

edit: upon answering the call, the green light turns off for a fraction of the second and then turns on.

Okay, so after some testing I see this problem for a call from android to iOS but not the other way around.

@khushal87 I did not kill the apps before testing the other way around so I guess you're correct.

@khushal87 you're right, tested it on my app and it's only Android -> iOS.

When I hang up on Android though I saw an empty screen, for some reason it fails to unmount. In other words, the device being "called" does not unmount the screen on hang up.

When I hang up on Android though I saw an empty screen, for some reason it fails to unmount. In other words, the device being "called" does not unmount the screen on hang up.

Not able to triage it on our DF app.

@khushal87 hi, is there any progress in this? I just installed "@stream-io/video-react-native-sdk": "0.6.13" and I'm still encountering this.

Hey @efstathiosntonas, this is already on our task list. We will try to get back on this soon.

This will be fixed in #1345