Skip to content

Commit 4aa7aa4

Browse files
fix(replay): Use software canvas for the video encoder on the Android Emulator
Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
1 parent b731c0b commit 4aa7aa4

2 files changed

Lines changed: 80 additions & 18 deletions

File tree

‎sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt‎

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,8 @@ internal class SimpleVideoEncoder(
177177
}
178178

179179
fun encode(image: Bitmap) {
180-
/**
181-
* it seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
182-
* instead https://stackoverflow.com/a/73520742
183-
* ---
184-
* Same for Motorola devices.
185-
* ---
186-
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
187-
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
188-
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
189-
*/
190180
val canvas =
191-
if (
192-
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
193-
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
194-
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
195-
.equals("spreadtrum", ignoreCase = true) ||
196-
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
197-
.equals("unisoc", ignoreCase = true)
198-
) {
181+
if (shouldUseSoftwareCanvas()) {
199182
surface?.lockCanvas(null)
200183
} else {
201184
surface?.lockHardwareCanvas()
@@ -338,6 +321,34 @@ internal class SimpleVideoEncoder(
338321
}
339322
}
340323

324+
/**
325+
* Whether frames should be drawn onto the encoder's input surface with a software canvas
326+
* ([Surface.lockCanvas]) instead of a hardware one ([Surface.lockHardwareCanvas]).
327+
*
328+
* It seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
329+
* instead https://stackoverflow.com/a/73520742
330+
* ---
331+
* Same for Motorola devices.
332+
* ---
333+
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
334+
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
335+
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
336+
* ---
337+
* On the Android Emulator (goldfish/ranchu), a hardware canvas makes hwui own an EGL window surface
338+
* on the input surface. The emulator's EGL driver keeps a dequeued buffer on it and cancels that
339+
* buffer when the surface is destroyed, which can crash natively in eglDestroySurface once the
340+
* encoder has been torn down. https://github.com/getsentry/sentry-java/issues/6279
341+
*/
342+
internal fun shouldUseSoftwareCanvas(): Boolean =
343+
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
344+
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
345+
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
346+
.equals("spreadtrum", ignoreCase = true) ||
347+
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
348+
.equals("unisoc", ignoreCase = true) ||
349+
Build.HARDWARE.contains("goldfish", ignoreCase = true) ||
350+
Build.HARDWARE.contains("ranchu", ignoreCase = true)
351+
341352
@SuppressLint("UseRequiresApi")
342353
@TargetApi(24)
343354
internal data class MuxerConfig(
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.sentry.android.replay.video
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.google.common.truth.Truth.assertThat
5+
import kotlin.test.Test
6+
import org.junit.runner.RunWith
7+
import org.robolectric.annotation.Config
8+
import org.robolectric.shadows.ShadowBuild
9+
10+
@RunWith(AndroidJUnit4::class)
11+
@Config(sdk = [30])
12+
class SimpleVideoEncoderCanvasTest {
13+
14+
@Test
15+
fun `uses hardware canvas on regular devices`() {
16+
ShadowBuild.setManufacturer("Google")
17+
ShadowBuild.setHardware("tensor")
18+
19+
assertThat(shouldUseSoftwareCanvas()).isFalse()
20+
}
21+
22+
@Test
23+
fun `uses software canvas on Xiaomi devices`() {
24+
ShadowBuild.setManufacturer("Xiaomi")
25+
26+
assertThat(shouldUseSoftwareCanvas()).isTrue()
27+
}
28+
29+
@Test
30+
fun `uses software canvas on Motorola devices`() {
31+
ShadowBuild.setManufacturer("motorola")
32+
33+
assertThat(shouldUseSoftwareCanvas()).isTrue()
34+
}
35+
36+
@Test
37+
fun `uses software canvas on ranchu emulators`() {
38+
ShadowBuild.setManufacturer("Google")
39+
ShadowBuild.setHardware("ranchu")
40+
41+
assertThat(shouldUseSoftwareCanvas()).isTrue()
42+
}
43+
44+
@Test
45+
fun `uses software canvas on goldfish emulators`() {
46+
ShadowBuild.setManufacturer("unknown")
47+
ShadowBuild.setHardware("goldfish")
48+
49+
assertThat(shouldUseSoftwareCanvas()).isTrue()
50+
}
51+
}

0 commit comments

Comments
 (0)