Skip to content
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

fix(replays): sync replay timer with shown duration #69074

Merged
merged 5 commits into from
Apr 17, 2024

Conversation

michellewzhang
Copy link
Member

@michellewzhang michellewzhang commented Apr 16, 2024

This PR fixes 2 bugs related to the video replay timer:

SCR-20240416-oqes

1. If the replay has a gap at the end (e.g. the segment ends but for some reason the duration we get from the SDK extends beyond that), the old behavior was that the replay would just stop.

BEFORE:

Screen.Recording.2024-04-16.at.4.17.02.PM.mov

Notice how the replay stops after 9 seconds. This is because the rest of the replay (the other 4 hours) is junk. The ideal behavior would be to have the replay play until the end, so that it doesn't look weird on the UI.

AFTER:

Screen.Recording.2024-04-16.at.4.20.36.PM.mov

Notice how the replay keeps going after 9 seconds (even though technically there isn't any video content to display. It's just showing the previous segment)

2. If we seek into the gap at the end of the video, the old behavior of the video replayer was to keep running the timer infinitely. This is because the replayer wanted to run the timer until we can play the next segment (which doesn't exist in this case).

BEFORE (video trimmed because you'd be watching paint dry but i seeked to somewhere about 10s from the end):

before.mov

Notice how the timer continues running even after the replay "ends".

The fix here is to add a notification to the timer to stop at the replay duration (passed in from replayContext) when we seek into a gap at the end. This lets the replayer know that we should stop searching for the next (nonexistent) segment.

AFTER (video trimmed, same seeking place):

after.mov

Video ends at the expected timestamp.

Fixes #68496
Also fixes #68509

@michellewzhang michellewzhang requested a review from a team as a code owner April 16, 2024 23:29
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 16, 2024
Copy link

codecov bot commented Apr 16, 2024

Bundle Report

Changes will decrease total bundle size by 106.54kB ⬇️

Bundle name Size Change
app-webpack-bundle-array-push 26.19MB 106.54kB ⬇️

Copy link
Member

@billyvg billyvg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some tests, but otherwise LGTM!

Comment on lines 304 to 309
// If we're at the end of a segment, but there's a gap
// at the end, force the replay to play until the end duration
// rather than stopping right away.
this._timer.addNotificationAtTime(this._duration, () => {
this.stopReplay();
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a chance of a race condition here where by the time this notification is added, the timer is already passed duration and will continue on?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one relates to the case where the replay stops early because we've hit a gap/the end of the segment -- the old code here was just this.stopReplay(). i think we might be okay here because we don't want to stop early, but rather keep playing until the end, which means we haven't hit duration yet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about the case where we do not have a gap, and we want to stop the replay:

  • segment ends -> handleSegmentEnd is called
  • no next segment so this code block is called
  • add notification for duration (e.g. 20)
  • due to external factors (data, hardware, etc), the current time is > 20
  • ?? does the callback get fired in this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohhh i see what you're saying! let me add another check that stops the replay if the time is over

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something like

    // No more segments
    if (nextIndex >= this._attachments.length) {
      if (this.getCurrentTime() < this._durationMs) {
        // If we're at the end of a segment, but there's a gap
        // at the end, force the replay to play until the end duration
        // rather than stopping right away.
        this._timer.addNotificationAtTime(this._durationMs, () => {
          this.stopReplay();
        });
        return;
      }
      this.stopReplay();
    }

static/app/components/replays/replayContext.tsx Outdated Show resolved Hide resolved
Comment on lines +312 to +320
if (this.getCurrentTime() < this._durationMs) {
// If we're at the end of a segment, but there's a gap
// at the end, force the replay to play until the end duration
// rather than stopping right away.
this._timer.addNotificationAtTime(this._durationMs, () => {
this.stopReplay();
});
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this tested?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +425 to +441
// we're still within the last segment (5)
// @ts-expect-error private
expect(inst._currentIndex).toEqual(5);
expect(inst.getCurrentTime()).toEqual(40000);

// now we are in the gap
// timer should still be going since the duration is 50s
jest.advanceTimersByTime(5000);
// @ts-expect-error private
expect(inst._isPlaying).toEqual(true);

// a long time passes
// ensure the timer stops at the end duration (50s)
jest.advanceTimersByTime(60000);
expect(inst.getCurrentTime()).toEqual(50000);
// @ts-expect-error private
expect(inst._isPlaying).toEqual(false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@billyvg i tried to test that here

Copy link
Member

@billyvg billyvg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michellewzhang michellewzhang merged commit 48f51a9 into master Apr 17, 2024
39 checks passed
@michellewzhang michellewzhang deleted the mz/timestamp-fix branch April 17, 2024 20:41
@github-actions github-actions bot locked and limited conversation to collaborators May 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Scope: Frontend Automatically applied to PRs that change frontend components
Projects
None yet
2 participants