ilabs-flir 2.4.11 → 2.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,6 +64,7 @@ object FlirManager {
64
64
 
65
65
  fun setTextureCallback(callback: TextureUpdateCallback?) {
66
66
  textureCallback = callback
67
+ sdkManager?.setFrameConsumerActive(callback != null)
67
68
  }
68
69
 
69
70
  interface TemperatureUpdateCallback {
@@ -107,6 +108,7 @@ object FlirManager {
107
108
  sdkManager = FlirSdkManager.getInstance(context)
108
109
  sdkManager?.setListener(sdkListener)
109
110
  sdkManager?.initialize()
111
+ sdkManager?.setFrameConsumerActive(textureCallback != null)
110
112
 
111
113
  isInitialized = true
112
114
  Log.i(TAG, "FlirManager initialized")
@@ -56,6 +56,10 @@ public class FlirSdkManager {
56
56
  private final List<Identity> discoveredDevices = Collections.synchronizedList(new ArrayList<>());
57
57
  private final Map<String, DiscoveredCamera> discoveredCameras = Collections.synchronizedMap(new HashMap<>());
58
58
  private volatile Bitmap latestBitmap;
59
+ private final Bitmap[] bitmapRingBuffer = new Bitmap[3];
60
+ private int ringBufferIndex = 0;
61
+ private volatile boolean isFrameConsumerActive = false;
62
+ private int[] scalePixelBuffer = null;
59
63
  private volatile String currentPaletteName = "WhiteHot";
60
64
  private final AtomicBoolean isProcessingFrame = new AtomicBoolean(false);
61
65
  private boolean useHalfScale = false;
@@ -118,6 +122,22 @@ public class FlirSdkManager {
118
122
  }
119
123
  }
120
124
  };
125
+
126
+ // Auto-detect low-end device to enable useHalfScale
127
+ try {
128
+ android.app.ActivityManager.MemoryInfo mi = new android.app.ActivityManager.MemoryInfo();
129
+ android.app.ActivityManager activityManager = (android.app.ActivityManager) this.context.getSystemService(Context.ACTIVITY_SERVICE);
130
+ if (activityManager != null) {
131
+ activityManager.getMemoryInfo(mi);
132
+ boolean isLowRam = activityManager.isLowRamDevice();
133
+ if (isLowRam || mi.totalMem < 3L * 1024 * 1024 * 1024) {
134
+ useHalfScale = true;
135
+ Log.i(TAG, "Low-end device detected (Total RAM: " + (mi.totalMem / (1024 * 1024)) + "MB). Enabling half-scale rendering by default.");
136
+ }
137
+ }
138
+ } catch (Throwable t) {
139
+ Log.e(TAG, "Failed to auto-detect memory constraints", t);
140
+ }
121
141
  }
122
142
 
123
143
  public static synchronized FlirSdkManager getInstance(Context context) {
@@ -145,6 +165,25 @@ public class FlirSdkManager {
145
165
  try {
146
166
  android.os.Looper.loop();
147
167
  } catch (Throwable t) {
168
+ if (t instanceof VirtualMachineError) {
169
+ Log.e(TAG, "🚨 [FLIR LOOPER PROTECTOR] VirtualMachineError/OOM detected! Releasing display bitmap and running GC...");
170
+ try {
171
+ if (instance != null) {
172
+ synchronized (instance) {
173
+ for (int i = 0; i < 3; i++) {
174
+ if (instance.bitmapRingBuffer[i] != null) {
175
+ instance.bitmapRingBuffer[i].recycle();
176
+ instance.bitmapRingBuffer[i] = null;
177
+ }
178
+ }
179
+ instance.latestBitmap = null;
180
+ instance.scalePixelBuffer = null;
181
+ }
182
+ }
183
+ System.gc();
184
+ } catch (Throwable ignored) {}
185
+ continue;
186
+ }
148
187
  String msg = t.getMessage();
149
188
  boolean isSuppressed = false;
150
189
 
@@ -425,6 +464,11 @@ public class FlirSdkManager {
425
464
  });
426
465
  }
427
466
 
467
+ public void setFrameConsumerActive(boolean active) {
468
+ this.isFrameConsumerActive = active;
469
+ Log.d(TAG, "isFrameConsumerActive set to: " + active);
470
+ }
471
+
428
472
  private void startStreamInternal() {
429
473
  if (camera == null) {
430
474
  notifyError("Not connected");
@@ -507,11 +551,13 @@ public class FlirSdkManager {
507
551
  synchronized (FlirSdkManager.this) {
508
552
  if (streamer != null && activeStream != null) {
509
553
  streamer.update();
510
-
511
554
  final String paletteToApply = currentPaletteName;
512
555
  final String snapshotPath = pendingSnapshotPath;
513
556
  pendingSnapshotPath = null;
514
557
  streamer.withThermalImage(thermalImage -> {
558
+ if (!isFrameConsumerActive && snapshotPath == null) {
559
+ return;
560
+ }
515
561
  // 1. Apply Palette
516
562
  if (paletteToApply != null) {
517
563
  try {
@@ -584,22 +630,79 @@ public class FlirSdkManager {
584
630
  }
585
631
 
586
632
  // 3. Generate Bitmap for display
587
- // We use streamer.getImage() to get the rendered image with palette applied.
588
- try {
589
- Bitmap newBitmap = BitmapAndroid.createBitmap(streamer.getImage()).getBitMap();
590
- if (newBitmap != null) {
591
- Bitmap oldBitmap = latestBitmap;
592
- latestBitmap = newBitmap;
593
- if (listener != null) {
594
- listener.onFrame(newBitmap);
595
- }
596
- // Recycle old bitmap to prevent memory leak
597
- if (oldBitmap != null && oldBitmap != newBitmap) {
598
- oldBitmap.recycle();
633
+ if (isFrameConsumerActive) {
634
+ try {
635
+ ImageBuffer imageBuffer = streamer.getImage();
636
+ if (imageBuffer != null) {
637
+ int width = imageBuffer.getWidth();
638
+ int height = imageBuffer.getHeight();
639
+ if (width > 0 && height > 0) {
640
+ synchronized (FlirSdkManager.this) {
641
+ int dstW = useHalfScale ? (width / 2) : width;
642
+ int dstH = useHalfScale ? (height / 2) : height;
643
+
644
+ for (int i = 0; i < 3; i++) {
645
+ if (bitmapRingBuffer[i] == null ||
646
+ bitmapRingBuffer[i].getWidth() != dstW ||
647
+ bitmapRingBuffer[i].getHeight() != dstH) {
648
+
649
+ if (bitmapRingBuffer[i] != null) {
650
+ bitmapRingBuffer[i].recycle();
651
+ }
652
+ bitmapRingBuffer[i] = Bitmap.createBitmap(dstW, dstH, Bitmap.Config.ARGB_8888);
653
+ }
654
+ }
655
+ ringBufferIndex = (ringBufferIndex + 1) % 3;
656
+ Bitmap targetBitmap = bitmapRingBuffer[ringBufferIndex];
657
+
658
+ if (useHalfScale) {
659
+ imageBuffer.with(new com.flir.thermalsdk.utils.Consumer<java.nio.ByteBuffer>() {
660
+ @Override
661
+ public void accept(java.nio.ByteBuffer byteBuffer) {
662
+ if (byteBuffer != null) {
663
+ byteBuffer.rewind();
664
+ java.nio.IntBuffer srcPixels = byteBuffer.asIntBuffer();
665
+
666
+ int totalPixels = dstW * dstH;
667
+ if (scalePixelBuffer == null || scalePixelBuffer.length != totalPixels) {
668
+ scalePixelBuffer = new int[totalPixels];
669
+ }
670
+
671
+ for (int y = 0; y < dstH; y++) {
672
+ int srcY = y * 2;
673
+ int srcRowOffset = srcY * width;
674
+ int dstRowOffset = y * dstW;
675
+ for (int x = 0; x < dstW; x++) {
676
+ int srcX = x * 2;
677
+ scalePixelBuffer[dstRowOffset + x] = srcPixels.get(srcRowOffset + srcX);
678
+ }
679
+ }
680
+ targetBitmap.setPixels(scalePixelBuffer, 0, dstW, 0, 0, dstW, dstH);
681
+ }
682
+ }
683
+ });
684
+ } else {
685
+ imageBuffer.with(new com.flir.thermalsdk.utils.Consumer<java.nio.ByteBuffer>() {
686
+ @Override
687
+ public void accept(java.nio.ByteBuffer byteBuffer) {
688
+ if (byteBuffer != null) {
689
+ byteBuffer.rewind();
690
+ targetBitmap.copyPixelsFromBuffer(byteBuffer);
691
+ }
692
+ }
693
+ });
694
+ }
695
+
696
+ latestBitmap = targetBitmap;
697
+ if (listener != null) {
698
+ listener.onFrame(targetBitmap);
699
+ }
700
+ }
701
+ }
599
702
  }
703
+ } catch (Exception e) {
704
+ Log.e(TAG, "Bitmap buffer copy failed", e);
600
705
  }
601
- } catch (Exception e) {
602
- Log.e(TAG, "Bitmap creation failed", e);
603
706
  }
604
707
  });
605
708
  }
@@ -641,7 +744,14 @@ public class FlirSdkManager {
641
744
  activeStream = null;
642
745
  }
643
746
  streamer = null;
747
+ for (int i = 0; i < 3; i++) {
748
+ if (bitmapRingBuffer[i] != null) {
749
+ bitmapRingBuffer[i].recycle();
750
+ bitmapRingBuffer[i] = null;
751
+ }
752
+ }
644
753
  latestBitmap = null;
754
+ scalePixelBuffer = null;
645
755
  }
646
756
  Log.d(TAG, "Streaming stopped");
647
757
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilabs-flir",
3
- "version": "2.4.11",
3
+ "version": "2.4.12",
4
4
  "description": "FLIR Thermal SDK for React Native - iOS & Android (bundled at compile time via postinstall)",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -67,4 +67,4 @@
67
67
  "sourceDir": "./android/Flir"
68
68
  }
69
69
  }
70
- }
70
+ }