Skip to content

Commit 2a07609

Browse files
fix: Infinite Session Replay Processing Loop (#5765)
Fix an infinite processing loop in SentryOnDemandReplay when not being able to read an image. Fixes GH-5757
1 parent cc02c0d commit 2a07609

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Fixes
6+
7+
- Fix Infinite Session Replay Processing Loop (#5765)
8+
39
## 8.54.0
410

511
### Fixes

Sources/Swift/Integrations/SessionReplay/SentryOnDemandReplay.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,14 @@ import UIKit
224224
// swiftlint:disable function_body_length cyclomatic_complexity
225225
private func renderVideo(with videoFrames: [SentryReplayFrame], from: Int, at outputFileURL: URL, completion: @escaping (Result<SentryRenderVideoResult, Error>) -> Void) {
226226
SentrySDKLog.debug("[Session Replay] Rendering video with \(videoFrames.count) frames, from index: \(from), to output url: \(outputFileURL)")
227+
227228
guard from < videoFrames.count else {
228229
SentrySDKLog.error("[Session Replay] Failed to render video, reason: index out of bounds")
229-
return completion(.success(SentryRenderVideoResult(
230-
info: nil,
231-
finalFrameIndex: from
232-
)))
230+
return completion(.failure(SentryOnDemandReplayError.indexOutOfBounds))
233231
}
234232
guard let image = UIImage(contentsOfFile: videoFrames[from].imagePath) else {
235233
SentrySDKLog.error("[Session Replay] Failed to render video, reason: can't read image at path: \(videoFrames[from].imagePath)")
236-
return completion(.success(SentryRenderVideoResult(
237-
info: nil,
238-
finalFrameIndex: from
239-
)))
234+
return completion(.failure(SentryOnDemandReplayError.cantReadImage))
240235
}
241236

242237
let videoWidth = image.size.width * CGFloat(videoScale)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
enum SentryOnDemandReplayError: Error {
22
case cantReadVideoSize
33
case cantCreatePixelBuffer
4+
case cantReadImage
45
case errorRenderingVideo
56
case cantReadVideoStartTime
7+
case indexOutOfBounds
68
}

Tests/SentryTests/Integrations/SessionReplay/SentryOnDemandReplayTests.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,40 @@ class SentryOnDemandReplayTests: XCTestCase {
243243
// -- Assert --
244244
XCTAssertEqual(videos.count, 0)
245245
}
246-
246+
247+
func testCreateVideo_DeleteFrameImages_NoVideoCreated() throws {
248+
// -- Arrange --
249+
let processingQueue = SentryDispatchQueueWrapper()
250+
let workerQueue = SentryDispatchQueueWrapper()
251+
let sut = SentryOnDemandReplay(
252+
outputPath: outputPath.path,
253+
processingQueue: processingQueue,
254+
assetWorkerQueue: workerQueue
255+
)
256+
257+
let start = Date(timeIntervalSinceReferenceDate: 0)
258+
sut.addFrameAsync(timestamp: start, maskedViewImage: UIImage.add)
259+
260+
processingQueue.dispatchSync {
261+
// Wait for the frame to be added by adding a sync operation to the serial queue
262+
}
263+
let end = start.addingTimeInterval(10)
264+
265+
// Delete the image added above so that reading the image at the image path fails,
266+
// because it's removed.
267+
let fileManager = FileManager.default
268+
let contents = try fileManager.contentsOfDirectory(at: outputPath, includingPropertiesForKeys: nil)
269+
for fileURL in contents {
270+
try fileManager.removeItem(at: fileURL)
271+
}
272+
273+
// -- Act --
274+
let result = sut.createVideoWith(beginning: start, end: end)
275+
276+
// -- Assert --
277+
XCTAssertEqual(result.count, 0)
278+
}
279+
247280
func testCalculatePresentationTime_withOneFPS_shouldReturnTiming() {
248281
// -- Arrange --
249282
let framesPerSecond = 1

0 commit comments

Comments
 (0)