28 lines
1.2 KiB
JavaScript
Raw Normal View History

// Independent cleanup attempts: a broken optional recorder or audio node must
// never keep the camera/microphone open or prevent the remaining owners closing.
const attempt=operation=>{try{operation();}catch{/* Continue releasing other owners. */}};
export function stopTracks(stream) {
if(!stream)return;
for(const track of stream.getTracks())attempt(()=>{track.onended=null;track.stop();});
}
export function releaseCapture(run,preview,finishRecording) {
if(run.released)return;
run.released=true;
attempt(()=>stopTracks(run.stream));
for(const timer of [run.timer,run.setupTimer,run.signalTimer])clearTimeout(timer);
clearInterval(run.updateTimer);
if(run.callback!=null)attempt(()=>preview.cancelVideoFrameCallback(run.callback));
if(run.recording)attempt(finishRecording);
if(run.node) {
run.node.onprocessorerror=null;
run.node.port.onmessage=null;
attempt(()=>run.node.port.postMessage('stop'));
attempt(()=>run.node.disconnect());
}
attempt(()=>run.source?.disconnect());
// A rejected or unresolved AudioContext.close never postpones track release.
attempt(()=>run.context?.close().catch(()=>{}));
}