-
Notifications
You must be signed in to change notification settings - Fork 48
feat(sr): Support image capture in Session Replay #813
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
Conversation
Current support for Android image processing is very slow, but confined to a background thread on Android. However, because we halt collection until individual images are processed, this will negatively affect replays with a large number of images. We'll need to look for an async method of processing images or speed it up significantly on Android refs: RUM-11125 # Conflicts: # packages/datadog_session_replay/example/lib/app.dart # packages/datadog_session_replay/example/lib/routes.dart # packages/datadog_session_replay/example/lib/screens/main_screen.dart
1b893f4
to
7e8c260
Compare
5a35f05
to
efdc738
Compare
val resourceWriter: ResourceWriter, | ||
val bitmapHandler: BitmapHandler = DefaultBitmapHandler(internalLogger) | ||
) : ResourceResolver { | ||
private val resourceKeyMap: MutableMap<Int, ResourceResolver.ResourceEntry> = mutableMapOf() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like items are added to resourceKeyMap
, but I don't see where they are cleared from it. Also, should we limit the size of the map? Same questions about knownResources
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we're not clearing them, except at the end of the session. I'd have to think about under what circumstances we wanted to clear them. What criteria does the Android SDK use? I didn't see it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll probably do that as a separate task. Created RUM-11500 to track
...lay/android/src/main/kotlin/com/datadoghq/flutter/sessionreplay/resource/ResourceResolver.kt
Show resolved
Hide resolved
...ndroid/src/test/kotlin/com/datadoghq/flutter/sessionreplay/FlutterSessionReplayBridgeTest.kt
Outdated
Show resolved
Hide resolved
...ndroid/src/test/kotlin/com/datadoghq/flutter/sessionreplay/FlutterSessionReplayBridgeTest.kt
Outdated
Show resolved
Hide resolved
...android/src/test/kotlin/com/datadoghq/flutter/sessionreplay/resource/ResourceResolverTest.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a few nits, feel free to disregard them. LGMT! 🚀
packages/datadog_session_replay/lib/src/ios/datadog_session_replay_platform_ios.dart
Outdated
Show resolved
Hide resolved
...tadog_session_replay/ios/datadog_session_replay/Sources/Swift/Resource/ResouceResolver.swift
Outdated
Show resolved
Hide resolved
...tadog_session_replay/ios/datadog_session_replay/Sources/Swift/Resource/ResourcesWriter.swift
Outdated
Show resolved
Hide resolved
...ession_replay/ios/datadog_session_replay/Sources/Swift/Resource/ResourceRequestBuilder.swift
Show resolved
Hide resolved
internal var mimeType: String | ||
internal var context: Context | ||
|
||
internal init( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't think this init is needed; since EnrichedResource
is a struct
, you get memberwise initializer for free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why this is needed, but it is apparently?
...lay/android/src/main/kotlin/com/datadoghq/flutter/sessionreplay/resource/ResourceResolver.kt
Outdated
Show resolved
Hide resolved
packages/datadog_session_replay/lib/src/capture/element_recorders/image_recorder.dart
Outdated
Show resolved
Hide resolved
...on_replay/ios/datadog_session_replay/Sources/Swift/Feature/FlutterSeasionReplayFeature.swift
Show resolved
Hide resolved
b50bab8
to
5f4505f
Compare
5f4505f
to
3d2e603
Compare
What and why?
Allow Session Replay tree traversal to capture images from Session Replay.
This works by using
RawImage
'stoByteData(format: ImageByteFormat.rawRgba)
method, which gives a raw, RGBA image with premultiplied alpha. This is then copied directly to the native layer for processing.On Android, we transfer the data using a
ByteBuffer
, and then use that buffer to create aBitmap
for compression and hashing. Because Android's raw bitmap format is also RGBA premultiplied (despite what the constant says), we can simply pass in the wrapped buffer to create the requiredBitmap
On iOS, we create
Data
/NSData
which copies the data directly from the buffer pointer. Doing this relatively safely requires we bypass theobjective_c
package, as usingaddress
in Dart requires calling an FFI leaf function (one that can be guaranteed not to call back into Dart) directly.Because of the memory constraints of the copy, we are limiting copies to 640,000 pixels (an 800x800 image), which would be a raw size of 2.4 MB.
Any images larger than that are captured as "Large Image", shown below.

This still needs to support caching which images we've sent between sessions.
Review checklist