-
Notifications
You must be signed in to change notification settings - Fork 216
video-6336: media element leak causes error on chrome 92 #1530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
lib/media/track/mediatrack.js
Outdated
@@ -118,24 +119,29 @@ class MediaTrack extends Track { | |||
* @private | |||
*/ | |||
_initialize() { | |||
const self = this; | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need to keep this try catch block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. This fix ensures that we clean up the dummy element. but customers would still see the problem if they manage to create 75+ elements (possible on large rooms). In such cases its good to log the error for further confirmation and of the issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we throw a TwilioError instead? That way, it follows our convention when an error happens.
this._dummyEl.oncanplay = null; | ||
this._dummyEl = null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one @makarandp0 . So we are only detaching elements, not properly disposing them. I've learned that we also need to pause the element first, then, call load after setting the srcObject to null. Something like
this._detachElementInternal(this._dummyEl);
this._dummyEl.pause();
this._dummyEl.remove();
this._dummyEl.srcObject = null;
this._dummyEl.oncanplay = null;
this._dummyEl.load();
this._dummyEl = null;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, I will update and test per your suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After experimenting a bit, I found that just setting srcObject to null is sufficient to avoid this issue. I will keep this simple and just reset srcObject
as suggested as a workaround:
lib/media/track/mediatrack.js
Outdated
? () => playIfPausedAndNotBackgrounded(el, this._log) | ||
: null; | ||
this._elShims.set(el, shimMediaElement(el, onUnintentionallyPaused)); | ||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this try catch block for debugging? Should we remove this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep this block, since the issue will still happen when 75+ elements are created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we throw a TwilioError instead? That way, it follows our convention when an error happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TwilioErrors are heavy - require associated documentation and such. I think this error is fine for now. I am also hoping that this issue will get fixed by chrome as lot of apps are affected by it. https://bugs.chromium.org/p/chromium/issues/detail?id=1232649
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: I found that the try block is not very useful as the error happens async and not caught by the block :(. I will remove the try block.
lib/media/track/mediatrack.js
Outdated
@@ -145,27 +151,35 @@ class MediaTrack extends Track { | |||
_end() { | |||
this._log.debug('Ended'); | |||
if (this._dummyEl) { | |||
this._detachElement(this._dummyEl); | |||
this._detachElementInternal(this._dummyEl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to create a separate _detachElementInternal
method? So on _end
, calling _detachElementInternal
will not update internal properties such as _attachments
and _elShims
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need seperate function because _detachElement
is no-op for _dummyEl
as its not added to this._attachments
It was a bug that caused us to never detach _dummyEl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good one.
@@ -145,8 +145,10 @@ class MediaTrack extends Track { | |||
_end() { | |||
this._log.debug('Ended'); | |||
if (this._dummyEl) { | |||
this._detachElement(this._dummyEl); | |||
this._dummyEl.remove(); | |||
this._dummyEl.srcObject = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just want to double check. Since we're not calling detachElement anymore, is setting srcObject to null enough, and we don't need to call removeTrack
?
const mediaStream = this._dummyEl.srcObject;
if (mediaStream instanceof this._MediaStream) {
mediaStream.removeTrack(this.processedTrack || this.mediaStreamTrack);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thats correct, removeTrack applies only for elements attached by application - since they may already have a mediaStream, in that case we add/remove track from it.
chrome 92 started limiting the WebMediaPlayers. This breaks lot of webrtc applications. This fix ensures that 1) we do not leak media elements created internally and 2) we clear the
srcObject
before removing media elements.The application that use
RemoteAudioTrack.detach
() method need to ensure that they clear srcObject on the returned elements as well.related github issues: #1367 & #1528
Contributing to Twilio