Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.1.44

- **Critical Bug Fix**: Fix SIGSEGV crash when YOLOView is disposed while TensorFlow Lite inference is running
- **Root Cause**: Race condition where `onFrame` callback continued executing after `stop()` cleared resources and closed the TensorFlow Lite interpreter
- **Fix**: Added `@Volatile` `isStopped` flag that is checked at multiple points in `onFrame` to prevent accessing closed resources

## 0.1.43

- **Enhancement**: Unify classification output format across all platforms to use official Results.summary() format
Expand Down
41 changes: 39 additions & 2 deletions android/src/main/kotlin/com/ultralytics/yolo/YOLOView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ class YOLOView @JvmOverloads constructor(

// New fields for proper teardown:
private var cameraExecutor: ExecutorService? = null
private var imageAnalysisUseCase: ImageAnalysis? = null
private var imageAnalysisUseCase: ImageAnalysis? = null

// Flag to track if the view is stopped/disposed to prevent race conditions
@Volatile
private var isStopped = false

// Zoom related
private var currentZoomRatio = 1.0f
Expand Down Expand Up @@ -511,6 +515,9 @@ class YOLOView @JvmOverloads constructor(

fun startCamera() {
Log.d(TAG, "Starting camera...")

// Reset stopped flag when restarting camera
isStopped = false

try {
cameraProviderFuture = ProcessCameraProvider.getInstance(context)
Expand Down Expand Up @@ -619,6 +626,13 @@ class YOLOView @JvmOverloads constructor(
// region onFrame (per frame inference)

private fun onFrame(imageProxy: ImageProxy) {
// Early return if view is stopped to prevent accessing closed resources
if (isStopped) {
Log.d(TAG, "onFrame: View is stopped, skipping frame processing")
imageProxy.close()
return
}

val w = imageProxy.width
val h = imageProxy.height
val orientation = context.resources.configuration.orientation
Expand All @@ -630,7 +644,21 @@ class YOLOView @JvmOverloads constructor(
return
}

// Check again after bitmap conversion (in case stop() was called during conversion)
if (isStopped) {
Log.d(TAG, "onFrame: View stopped during bitmap conversion, skipping inference")
imageProxy.close()
return
}

predictor?.let { p ->
// Double-check stopped flag before inference (predictor might be closed)
if (isStopped) {
Log.d(TAG, "onFrame: View stopped before inference, skipping")
imageProxy.close()
return
}

// Check if we should run inference on this frame
if (!shouldRunInference()) {
Log.d(TAG, "Skipping inference due to frequency control")
Expand Down Expand Up @@ -1773,6 +1801,9 @@ class YOLOView @JvmOverloads constructor(
*/
fun stop() {
Log.d(TAG, "YOLOView.stop() called - tearing down camera")

// Set stopped flag first to prevent new frames from being processed
isStopped = true

try {
imageAnalysisUseCase?.clearAnalyzer()
Expand Down Expand Up @@ -1811,7 +1842,13 @@ class YOLOView @JvmOverloads constructor(
cameraExecutor = null

camera = null
(predictor as? BasePredictor)?.close()

// Close predictor safely - ensure no inference is running
try {
(predictor as? BasePredictor)?.close()
} catch (e: Exception) {
Log.e(TAG, "Error closing predictor", e)
}
predictor = null
inferenceCallback = null
streamCallback = null
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

name: ultralytics_yolo
description: Flutter plugin for Ultralytics YOLO computer vision models.
version: 0.1.43
version: 0.1.44
homepage: https://github.com/ultralytics/yolo-flutter-app
repository: https://github.com/ultralytics/yolo-flutter-app
issue_tracker: https://github.com/ultralytics/yolo-flutter-app/issues
Expand Down
Loading