Skip to content

Commit aa192a2

Browse files
authored
Merge pull request #9254 from slash-init/fix/create-capture-stream-leak
fix: stop pending createCapture media streams on remove
2 parents 3193703 + 8cdc1c4 commit aa192a2

2 files changed

Lines changed: 67 additions & 0 deletions

File tree

‎src/dom/p5.MediaElement.js‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,9 +1630,14 @@ function media(p5, fn) {
16301630
const domElement = document.createElement(VIDEO);
16311631
// required to work in iOS 11 & up:
16321632
domElement.setAttribute('playsinline', '');
1633+
const signal = this._removeSignal;
16331634
navigator.mediaDevices
16341635
.getUserMedia(constraints)
16351636
.then(function (stream) {
1637+
if (signal.aborted) {
1638+
stream.getTracks().forEach(track => track.stop());
1639+
return;
1640+
}
16361641
try {
16371642
if ('srcObject' in domElement) {
16381643
domElement.srcObject = stream;

‎test/unit/dom/dom.js‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,5 +1702,67 @@ suite('DOM', function () {
17021702
createElementSpy.mockRestore();
17031703
}
17041704
});
1705+
1706+
test('p5.remove() stops tracks when createCapture() is pending', async function () {
1707+
const tracks = [
1708+
{
1709+
readyState: 'live',
1710+
stop() {
1711+
this.readyState = 'ended';
1712+
}
1713+
},
1714+
{
1715+
readyState: 'live',
1716+
stop() {
1717+
this.readyState = 'ended';
1718+
}
1719+
}
1720+
];
1721+
const stream = { getTracks: () => tracks };
1722+
const originalGetUserMedia = navigator.mediaDevices.getUserMedia;
1723+
const createElement = document.createElement.bind(document);
1724+
let resolveGetUserMedia;
1725+
let myp5;
1726+
1727+
navigator.mediaDevices.getUserMedia = () =>
1728+
new Promise(resolve => {
1729+
resolveGetUserMedia = resolve;
1730+
});
1731+
const createElementSpy = vi
1732+
.spyOn(document, 'createElement')
1733+
.mockImplementation(tagName => {
1734+
const element = createElement(tagName);
1735+
if (tagName.toLowerCase() === 'video') {
1736+
let srcObject = null;
1737+
Object.defineProperty(element, 'srcObject', {
1738+
configurable: true,
1739+
get() {
1740+
return srcObject;
1741+
},
1742+
set(value) {
1743+
srcObject = value;
1744+
}
1745+
});
1746+
}
1747+
return element;
1748+
});
1749+
1750+
try {
1751+
myp5 = new p5(function () {});
1752+
myp5.createCanvas(100, 100);
1753+
const capture = myp5.createCapture(myp5.VIDEO);
1754+
await myp5.remove();
1755+
myp5 = null;
1756+
resolveGetUserMedia(stream);
1757+
await Promise.resolve();
1758+
1759+
assert.isTrue(tracks.every(track => track.readyState === 'ended'));
1760+
assert.isNull(capture.elt.srcObject);
1761+
} finally {
1762+
if (myp5) await myp5.remove();
1763+
navigator.mediaDevices.getUserMedia = originalGetUserMedia;
1764+
createElementSpy.mockRestore();
1765+
}
1766+
});
17051767
});
17061768
});

0 commit comments

Comments
 (0)