fix(01-review): sweep #1 stopRecording nulls mediaStream first to prevent rotation race

This commit is contained in:
2026-05-16 10:52:59 +02:00
parent a6e2d09de8
commit 08a79a61ac
2 changed files with 46 additions and 3 deletions

View File

@@ -359,16 +359,40 @@ function onUserStoppedSharing(): void {
}
function stopRecording(): void {
// Отменяем будущую ротацию, чтобы rotateSegment не дёрнул stop() на
// уже остановленном рекордере.
// Sweep #1 fix: race between rotateSegment / onSegmentStopped and a
// STOP_RECORDING that does NOT null the mediaStream. The previous
// implementation cleared the rotation timer and called .stop(), which
// triggers onSegmentStopped async; onSegmentStopped then unconditionally
// calls startNewSegment() whenever `mediaStream !== null`. Result: after
// a manual STOP_RECORDING, a new segment would spawn anyway and the
// recorder kept rolling silently. The fix mirrors onUserStoppedSharing:
// null the mediaStream FIRST so onSegmentStopped's gate sees the stopped
// state, then stop the recorder and release the tracks. This also fixes
// the secondary issue of leaked MediaStreamTracks (the picker indicator
// would have stayed up until the operator manually clicked "Stop
// sharing" in Chrome's UI).
if (rotationTimerId !== null) {
clearTimeout(rotationTimerId);
rotationTimerId = null;
}
// Capture stream before nulling so we can release its tracks.
const streamToStop = mediaStream;
mediaStream = null;
if (videoRecorder !== null && videoRecorder.state !== 'inactive') {
videoRecorder.stop();
try {
videoRecorder.stop();
} catch (err) {
logger.warn('stop() during stopRecording failed:', err);
}
logger.log('Recording stopped manually');
}
if (streamToStop !== null) {
streamToStop.getTracks().forEach((t) => t.stop());
}
// Note: we do NOT call resetBuffer() here — the operator may want to
// STOP and then SAVE the buffered footage. resetBuffer happens on
// onUserStoppedSharing (operator dismissed Chrome's sharing indicator)
// and at the start of the next startRecording().
}
// ─── Bootstrap: handshake + port lifecycle (D-17, RESEARCH.md Patterns 4 & 5) ──