# Will Producer.close() close the underlying MediaStreamTrack?

**URL:** <https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122>\
**Category:** Off Topic\
**Created:** [December 13, 2020, 11:01pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122 "2020-12-13T23:01:42Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [December 13, 2020, 11:01pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/1 "2020-12-13T23:01:42Z")

</div>

The reactjs app I am working on sometimes rerenders components reusing the same MediaStreamTrack. When I close the session (you can call it room or whatever) I invoke close in the websocket connection, producers, and transports (see below) but my browser notification and the camera indicator light is on (in some cases).

Playing around with it I discovered that actually invoking `track.stop` on the videoElement HTML component and `videoElement.current.srcObject = null` will close the indicator light of the camera after a couple of seconds (or more) which gives me the impression that `Producer.close()` does not! I just assumed it will.

```auto
if (videoElement.current.paused && !isVideoPlaying && videoCanPlay) {
        if (videoElement.current.srcObject) {
          if (videoElement.current.srcObject?.getVideoTracks()) {
            videoElement.current.srcObject.getVideoTracks().forEach((track) => {
              track.stop()
            })
          }
          videoElement.current.srcObject = null
        }

```

Could you please clarify if the MediaStreamTrack will actually close calling `Producer.close()`?

```auto
async close(): Promise<void> {
    if (this.protooPeer && !this.protooPeer.closed) {
      // Close protoo Peer
      this.protooPeer?.close()
    }

    // Close mediasoup Transports.
    if (this.sendTransport && !this.sendTransport.closed) {
      this.sendTransport.close()
      this.sendTransport = null
    }

    if (this.recvTransport && !this.recvTransport.closed) {
      this.recvTransport.close()
      this.recvTransport = null
    }

    // Close mediasoup Producers.
    if (this.micProducer && !this.micProducer.closed) {
      this.micProducer.close() // Will close audio MediaStreamTrack?
      this.micProducer = null
    }

    if (this.videoProducer && !this.videoProducer.closed) {
      this.videoProducer.close() // Will close video MediaStreamTrack?
      this.videoProducer = null
    }
  }

```

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [December 13, 2020, 11:32pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/2 "2020-12-13T23:32:46Z")

</div>

We write documentation for something. Those are the `ProducerOptions`:

- [https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions](https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerOptions)

Also, checking the source code also helps:

- [https://github.com/versatica/mediasoup-client/blob/v3/src/Producer.ts#L233](https://github.com/versatica/mediasoup-client/blob/v3/src/Producer.ts#L233)

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [December 13, 2020, 11:57pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/3 "2020-12-13T23:57:21Z")

</div>

I will check my options, seems like the default should have closed it. Thanks.

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [December 14, 2020, 12:44am UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/4 "2020-12-14T00:44:50Z")

</div>

Yes, it should.

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [April 18, 2021, 6:07pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/5 "2021-04-18T18:07:23Z")

</div>

Revisiting this thread as I got back to try fixing it with no luck so far…

If you see the screenshot my tab camera use icon is still on after closing the producers and my actual computer camera is still on. Refreshing the tab will fix it so at this point I am open to suggestions on how to tackle this. See below what I tried.

I am using functional components and with useEffects and useState I set the MediaStreamTrack. e.g.

```auto
const [videoStreamTrack, setVideoStreamTrack] = useState<MediaStreamTrack>(
    null
  )

useEffect(() => {
    return () => {
      videoStreamTrack?.stop()
      videoElement.current.srcObject.getVideoTracks().forEach((track) => {
        track.stop()
        videoElement.current.srcObject.removeTrack(track)
      })
      videoElement.current.srcObject = null
    }
  }, [])

case RoomEvents.WebcamProducerAdded:
      case RoomEvents.VideoStreamReplaced: {
        if (result.data.stream) {
          const videoTrack = result.data.stream.getVideoTracks()[0]
          if (videoTrack) {
            setVideoStreamTrack(videoTrack) // in turn other effects will be invoked to play the track in video element
          }
        }
        break
      }

```

Closing the producer.  
Closing the track in the component when unmounted.  
Closing the track in the video element.

The camera light in my macbook and the icon in the tab still on…

![Screen Shot 2021-04-17 at 14.08.36](https://global.discourse-cdn.com/free1/uploads/mediasoup/original/1X/abda05393a9d3547de3f51bb162dd26a2d97de62.png)

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [April 19, 2021, 11:35am UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/6 "2021-04-19T11:35:14Z")

</div>

Since this is not really mediasoup related question I have created a SO post and paste it here for reference in case other people can help or get help.

> <https://stackoverflow.com/questions/67161217/closing-webrtc-track-will-not-close-camera-device-or-tab-camera-indicator>

---

<div class="post-metadata">

**Author:** ![BronzedBroth](https://avatars.discourse-cdn.com/v4/letter/b/ad7895/32.png) [@BronzedBroth](https://mediasoup.discourse.group/u/BronzedBroth)\
**Post date:** [April 20, 2021, 10:36pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/7 "2021-04-20T22:36:59Z")

</div>

If you do not close the transport you’re still connected and that’s what that live icon is.

1. Check if broadcast was made or exists enough to clear it.

2. Checking the mediastream for tracks opened to stop and unset them.

3. Last and but not least in this excitement of coarse would just be to close the transport and unset it.

If you’re switching out streams you won’t or shouldn’t need to close the transport. Consider this example one I use to 100% close streams. Through the Webrtc-internals you should see connection closed and any broadcasts on your end will stick around for a bit for diagnostic reasons as all consumes on clear will disappear from that list if done right.

Saw a few errors in your code so it’s not easy to really advise but to give a simple example, but do suggest reading the well documented API most of this is posted. Cheers.

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [April 21, 2021, 11:45am UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/8 "2021-04-21T11:45:54Z")

</div>

Thank you for the reply and for providing input so I can start getting to the solution.

Below is the code I am closing pretty much transports, producers, and the WebSocket connection.

```auto
if (this.protooPeer && !this.protooPeer.closed) {
      logger.info('will close protooPeer')

      // Close protoo Peer
      this.protooPeer?.close()
    }

    // Close mediasoup Transports.
    if (this.sendTransport && !this.sendTransport.closed) {
      logger.info('sendTransport close')
      this.sendTransport.close()
      this.sendTransport = null
    }

    if (this.recvTransport && !this.recvTransport.closed) {
      logger.info('recvTransport close')
      this.recvTransport.close()
      this.recvTransport = null
    }

    // Close mediasoup Producers.
    if (this.micProducer && !this.micProducer.closed) {
      logger.info('audio producer close')
      this.micProducer.close()
      this.micProducer = null
    }

    if (this.videoProducer && !this.videoProducer.closed) {
      logger.info('video producer close')
      this.videoProducer.close()
      this.videoProducer = null
    }

```

And the code I grab the track.

```auto
const userMedia = await navigator.mediaDevices.getUserMedia({
    video: this.environmentPlatformService.isMobile ?
        true :
        {
            deviceId: {
                exact: this.webcam.device.deviceId
            },
            ...VIDEO_CONSTRAINS[this.webcam.resolution],
        },
})
// This is the track used for broadcast in mediasoup and in HTML video element for local purposes
// I don't keep the userMedia variable anywhere (Code changed from previous snippet where I was passing the stream to components
const videoTrack = userMedia.getVideoTracks()[0]

this.videoProducer = await this.sendTransport?.produce({
        track: videoTrack,
        encodings,
        codecOptions: {
          videoGoogleStartBitrate: 1000,
        },
        codec,
      })

```

To your points

1. Check if broadcast was made or exists enough to clear it. - When you say broadcast can you be more specific, MediaStream, MediaStreamTrack, Producer;
2. Checking the mediastream for tracks opened to stop and unset them. - Where would you do that; since I don’t keep the stream around but just pass the track to the functional component I am doing this for the video element tracks and the react component state variable
3. Last and but not least in this excitement of coarse would just be to close the transport and unset it. - It is tough for me to understand the broadcast, transport, and handle types in your code. I posted my code for reference in the discussion

> If you’re switching out streams you won’t or shouldn’t need to close the transport.

I am switching streams from a preview track using the navigator API in reactjs component but stop and clear as soon as I have the stream from another call and configured with mediasoup `Transport` to create the video `Producer`.

> Saw a few errors in your code so it’s not easy to really advise but to give a simple example, but do suggest reading the well documented API most of this is posted. Cheers.

Please point out any error in the StackOverflow, it will greatly help me improve and find the issue. Open to suggestions.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [April 21, 2021, 12:59pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/9 "2021-04-21T12:59:01Z")

</div>

@xplatsolutions thought I’d offer some suggestions:

Producer.close will call track.stop for the currently associated track (unless you have explicitly disabled this functionality). You don’t need to look at any other mediasoup API to get this done.

Calling track.stop for all tracks returned from getUserMedia will disable the recording icon. That’s all you need to do. You don’t need to null any references.

The reason your recording icon persists is that you have a leftover track somewhere that hasn’t been closed. Have you perhaps used track.clone and forgotten to close the clone?

One way to identify your issue is to log all the track ids returned from getUserMedia, and then log all the track ids that you close. Compare the list, find out what you didn’t close, and that’ll give you an idea of what part of your code to look at.

Hope this helps!

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [April 21, 2021, 1:02pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/10 "2021-04-21T13:02:28Z")

</div>

Also, I see you storing userMedia and userMedia.getVideoTracks()[0]

Make sure you close ALL tracks returned from getUserMedia. userMedia wont be garbage collected if you maintain a reference to it, and you might still be maintaining unused tracks? It’s possible you have to close the tracks even if you don’t maintain a reference to the userMedia object. Are there any other video or audio tracks returned by getUserMedia? Do you make any other calls to getUserMedia? That might be where your issue lies.

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [April 22, 2021, 10:00am UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/11 "2021-04-22T10:00:30Z")

</div>

@dimoochka so many good suggestions and I believe I have plenty to work on the next couple of days to get to the bottom of it.

Actually, I just thought that I never close the audio track passed to the Producer and in this case, I use the MediaStream passed to other functional components.  
I guess it wouldn’t keep the camera device light indicator on but maybe the tab icon; Might be not related.

I will do the trick with the list as you said and the other suggestions and get back to this thread. Thanks!

---

<div class="post-metadata">

**Author:** ![BronzedBroth](https://avatars.discourse-cdn.com/v4/letter/b/ad7895/32.png) [@BronzedBroth](https://mediasoup.discourse.group/u/BronzedBroth)\
**Post date:** [April 22, 2021, 1:32pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/12 "2021-04-22T13:32:38Z")

</div>

I overviewed the stack slightly and it honestly looked like you may be calling audio and not closing all the tracks cause it shows all throughout just video, but you return audio in one of the functions, didn’t trace it though to see if you clear that. Curious if this was the case.

---

<div class="post-metadata">

**Author:** ![xplatsolutions](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/xplatsolutions/32/939_2.png) [@xplatsolutions](https://mediasoup.discourse.group/u/xplatsolutions)\
**Post date:** [May 2, 2021, 2:49pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/13 "2021-05-02T14:49:37Z")

</div>

I found the issues holding the MediaStreamTrack reference open. It wasn’t just one thing.

1. We are calling `getUserMedia` in multiple places not only in the “room” class handling `mediasoup` actions but also fore preview/device-selection/etc and didn’t really ever closed the tracks retrieved.
2. Sometimes, those tracks are held into `useState` variables and when component unmounted if you try to access the variables they are already nulled by `reactjs`. The workaround is since the HTML elements are still referenced stop the track when needed. I believe this was the missing ingredient when trying to figure it out.
3. Close video and audio tracks always (but of course not the one used in `mediasoup`). In any case I added additional code to make sure the `mediasoup` track is stopped when the session ended or stream track replaced and make sure the previous track stopped if exists.

Yes, audio matters for the tab indicator.

Thank you both for your help and suggestions. @dimoochka @BronzedBroth

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [May 2, 2021, 5:10pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/14 "2021-05-02T17:10:42Z")

</div>

Awesome, glad you got it figured out! Debugging is purgatory.

---

<div class="post-metadata">

**Author:** ![hassan](https://avatars.discourse-cdn.com/v4/letter/h/7993a0/32.png) [@hassan](https://mediasoup.discourse.group/u/hassan)\
**Post date:** [June 25, 2024, 7:39pm UTC](https://mediasoup.discourse.group/t/will-producer-close-close-the-underlying-mediastreamtrack/2122/15 "2024-06-25T19:39:42Z")

</div>

hi.to remove red circle after stop sharing. work correctly.  
with this : (videoTag is useRef in react js )

```auto
 if (videoTag.current.srcObject instanceof MediaStream) {
          videoTag.current.srcObject.getTracks().forEach(t => t.stop())
 }

```
