I'm building a continuous scanner using library version 4.2.0 and I found my device (Samsung S20, Android 11) quickly draining battery due to garbage collection. An older device with Android 6 throws error due to out of memory.
I'm using the CameraPreview class directly and request continuous previews using requestPreview(callback).
The byte array is allocated by the system and on my Samsung S20 is 1920x864, and 2488320 bytes (~2.3 MB). I assume this byte array is unowned and cannot be returned and will lie around after processing, waiting to be garbage collected. The SourceData's rotation is 90, and the cropRect is Rect(86, 614 - 777, 1305).
I then receive the SourceData and call .createSource() on it to get my PlanarYUVLuminanceSource. The RawImageData.rotateCW() then creates a complete new giant byte array with the rotated image data, which will be 1920x864 ~ 1.6 MB:
So for a single scan pass, my device has allocated 2.3 MB + 1.6 MB + 450 KB = 4.3 MB of memory that will have to be garbage collected. Targeting 10 passes / second I'd hit memory limits very quickly.
I feel this can be optimised:
Why don't we rotate after cropping and scaling? That way we'd already reduce memory footprint as the rotation image is smaller?
Why are we rotating at all? For the purpose of scanning QR or bar codes, is this even necessary?
At least the buffer after the rotation could be reused for the cropping and scaling?
The rotation could be optimised as well by rotating the buffer in-place? After all it is only swapping pixels and the SourceData object owns the data array at this point.
Description of the problem:
I'm building a continuous scanner using library version 4.2.0 and I found my device (Samsung S20, Android 11) quickly draining battery due to garbage collection. An older device with Android 6 throws error due to out of memory.
I'm using the
CameraPreview
class directly and request continuous previews usingrequestPreview(callback)
.From my understanding, this asks the Android system for a camera preview and receives it https://github.com/journeyapps/zxing-android-embedded/blob/40260272fcff4f14181803495e7d370c23e35db7/zxing-android-embedded/src/com/journeyapps/barcodescanner/camera/CameraManager.java#L93-L97
The byte array is allocated by the system and on my Samsung S20 is 1920x864, and 2488320 bytes (~2.3 MB). I assume this byte array is unowned and cannot be returned and will lie around after processing, waiting to be garbage collected. The
SourceData
'srotation
is 90, and thecropRect
isRect(86, 614 - 777, 1305)
.I then receive the
SourceData
and call.createSource()
on it to get myPlanarYUVLuminanceSource
. TheRawImageData.rotateCW()
then creates a complete new giant byte array with the rotated image data, which will be 1920x864 ~ 1.6 MB:https://github.com/journeyapps/zxing-android-embedded/blob/40260272fcff4f14181803495e7d370c23e35db7/zxing-android-embedded/src/com/journeyapps/barcodescanner/RawImageData.java#L87-L93
The
RawImageData.cropAndScale()
will then also create another byte buffer with 691x691 ~ 450 KB and will immediately drop the one from the rotation.https://github.com/journeyapps/zxing-android-embedded/blob/40260272fcff4f14181803495e7d370c23e35db7/zxing-android-embedded/src/com/journeyapps/barcodescanner/RawImageData.java#L28-L35
So for a single scan pass, my device has allocated 2.3 MB + 1.6 MB + 450 KB = 4.3 MB of memory that will have to be garbage collected. Targeting 10 passes / second I'd hit memory limits very quickly.
I feel this can be optimised:
SourceData
object owns the data array at this point.Any thoughts on this?