-
-
Notifications
You must be signed in to change notification settings - Fork 461
Description
Description
Consider the following code that creates segments in response to a screenshot being captured
Lines 75 to 124 in f6e97b1
| override fun onScreenshotRecorded(bitmap: Bitmap?, store: ReplayCache.(frameTimestamp: Long) -> Unit) { | |
| if (options.connectionStatusProvider.connectionStatus == DISCONNECTED) { | |
| options.logger.log(DEBUG, "Skipping screenshot recording, no internet connection") | |
| bitmap?.recycle() | |
| return | |
| } | |
| // have to do it before submitting, otherwise if the queue is busy, the timestamp won't be | |
| // reflecting the exact time of when it was captured | |
| val frameTimestamp = dateProvider.currentTimeMillis | |
| val height = recorderConfig.recordingHeight | |
| val width = recorderConfig.recordingWidth | |
| replayExecutor.submitSafely(options, "$TAG.add_frame") { | |
| cache?.store(frameTimestamp) | |
| val currentSegmentTimestamp = segmentTimestamp | |
| currentSegmentTimestamp ?: run { | |
| options.logger.log(DEBUG, "Segment timestamp is not set, not recording frame") | |
| return@submitSafely | |
| } | |
| if (isTerminating.get()) { | |
| options.logger.log(DEBUG, "Not capturing segment, because the app is terminating, will be captured on next launch") | |
| return@submitSafely | |
| } | |
| val now = dateProvider.currentTimeMillis | |
| if ((now - currentSegmentTimestamp.time >= options.experimental.sessionReplay.sessionSegmentDuration)) { | |
| val segment = | |
| createSegmentInternal( | |
| options.experimental.sessionReplay.sessionSegmentDuration, | |
| currentSegmentTimestamp, | |
| currentReplayId, | |
| currentSegment, | |
| height, | |
| width | |
| ) | |
| if (segment is ReplaySegment.Created) { | |
| segment.capture(hub) | |
| currentSegment++ | |
| // set next segment timestamp as close to the previous one as possible to avoid gaps | |
| segmentTimestamp = segment.replay.timestamp | |
| } | |
| } | |
| if ((now - replayStartTimestamp.get() >= options.experimental.sessionReplay.sessionDuration)) { | |
| options.replayController.stop() | |
| options.logger.log(INFO, "Session replay deadline exceeded (1h), stopping recording") | |
| } | |
| } | |
| } |
Specifically,
Lines 103 to 105 in f6e97b1
| createSegmentInternal( | |
| options.experimental.sessionReplay.sessionSegmentDuration, | |
| currentSegmentTimestamp, |
If there is a gap between frames of more than sessionSegmentDuration, a segment would be created only for the duration prior to this frame.
I've noticed this while investigating why breadcrumbs are sometimes not send on Flutter - it's because if there are no changes on the UI, there is no frame created but when next there are some changes (and breadcrumbs created for UI taps), these were not sent because:
- the current segment start timestamp was the end timestamp of the previous segment
- the duration of the segment was capped at the
sessionSegmentDuration(5 seconds at the moment)
thus when the current screenshot timestamp was, for example, a minute after the previous one (because the app sat idle), it would take additional 60/5 = 12 frames (12 seconds when the user actively did something on the UI) to catch up; or actually 12 + at least additional 3 for those 12 seconds
I've tried a minor change of passing the actual duration between the last segment end-timestamp and the current frame timestamp, although I'm not sure that is the way to go:
val now = dateProvider.currentTimeMillis
val duration = now - currentSegmentTimestamp.time
if ((duration >= options.experimental.sessionReplay.sessionSegmentDuration)) {
val segment =
createSegmentInternal(
duration,
currentSegmentTimestamp,Instead, I think it may be necessary to either loop here or add more job instances: replayExecutor.submitSafely(options, "$TAG.add_frame") {
Metadata
Metadata
Assignees
Labels
Projects
Status