Skip to content

Commit 51055a5

Browse files
committed
fix: improve pixel format selection for video output on iPhone 17
1 parent 3dc0823 commit 51055a5

File tree

3 files changed

+36
-45
lines changed

3 files changed

+36
-45
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 7.1.4
2+
3+
* [Apple] Fixed crash on iPhone 17 when starting MobileScanner by checking available pixel formats before setting video output settings. ([#1578](https://github.com/juliansteenbakker/mobile_scanner/issues/1578))
4+
15
### 7.1.3
26

37
* Overlay: Updated `BarcodePainter` to receive `deviceOrientation` and dynamically adjust `cameraPreviewSize`, fixing barcode overlay misalignment during device rotation changes [](https://github.com/juliansteenbakker/mobile_scanner/issues/1462).

darwin/mobile_scanner/Sources/mobile_scanner/MobileScannerPlugin.swift

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -455,53 +455,15 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
455455
}
456456
captureSession!.sessionPreset = AVCaptureSession.Preset.high
457457

458-
// Add video output
459458
let videoOutput = AVCaptureVideoDataOutput()
460-
461-
// Fix for iPhone 17: Check available pixel formats before setting
462-
// Issue: https://github.com/juliansteenbakker/mobile_scanner/issues/1578
463-
464-
// Define preferred pixel formats in order of preference
465-
let preferredFormats: [OSType] = [
466-
kCVPixelFormatType_32BGRA,
467-
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
468-
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
469-
]
470-
471-
// Get available formats and convert from NSNumber to OSType
472-
let availableFormats = videoOutput.availableVideoPixelFormatTypes
473-
let availablePixelFormats = availableFormats.compactMap { ($0 as NSNumber).uint32Value }
474-
475-
// Find the first preferred format that is available
476-
var selectedFormat: OSType?
477-
for format in preferredFormats {
478-
if availablePixelFormats.contains(format) {
479-
selectedFormat = format
480-
break
481-
}
482-
}
483-
484-
// Fallback: use first available format if no preferred format found
485-
if selectedFormat == nil, let firstAvailable = availablePixelFormats.first {
486-
selectedFormat = firstAvailable
487-
}
488-
489-
// Apply the selected format or fallback to the old default
490-
if let format = selectedFormat {
491-
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: format]
492-
} else {
493-
// Ultimate fallback: use the original default format
494-
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
495-
}
496-
497-
videoOutput.alwaysDiscardsLateVideoFrames = true
498459

460+
let format = getPreferredVideoFormat(videoOutput: videoOutput)
461+
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: format]
462+
videoOutput.alwaysDiscardsLateVideoFrames = true
499463
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
500464
captureSession!.addOutput(videoOutput)
501465
let deviceVideoOrientation = self.getVideoOrientation()
502-
503466

504-
// Adjust orientation for the video connection
505467
if let connection = videoOutput.connections.first {
506468
if connection.isVideoOrientationSupported {
507469
connection.videoOrientation = deviceVideoOrientation
@@ -514,7 +476,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
514476

515477
captureSession!.commitConfiguration()
516478

517-
// Move startRunning to a background thread to avoid blocking the main UI thread.
518479
DispatchQueue.global(qos: .background).async {
519480
self.captureSession!.startRunning()
520481

@@ -527,12 +488,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
527488
dimensions = CMVideoDimensions()
528489
}
529490

530-
// Turn on the torch if requested.
531491
if (torch) {
532492
self.turnTorchOn()
533493
}
534494

535-
// Set the initial zoom factor
536495
if (initialZoom != nil) {
537496
do {
538497
try self.setScaleInternal(initialZoom!)
@@ -579,6 +538,34 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
579538
}
580539
}
581540

541+
/// Get the preferred video format for the given video output.
542+
private func getPreferredVideoFormat(videoOutput: AVCaptureVideoDataOutput) -> OSType {
543+
// Define preferred pixel formats in order of preference
544+
let preferredFormats: [OSType] = [
545+
kCVPixelFormatType_32BGRA,
546+
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
547+
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
548+
]
549+
550+
// Get available formats and convert from NSNumber to OSType
551+
let availableFormats = videoOutput.availableVideoPixelFormatTypes
552+
let availablePixelFormats = availableFormats.compactMap { ($0 as NSNumber).uint32Value }
553+
554+
// Find the first preferred format that is available
555+
for format in preferredFormats {
556+
if availablePixelFormats.contains(format) {
557+
return format
558+
}
559+
}
560+
561+
if let firstAvailable = availablePixelFormats.first {
562+
return firstAvailable
563+
}
564+
565+
// Ultimate fallback: use the original default format
566+
return kCVPixelFormatType_32BGRA
567+
}
568+
582569
/// Turn the torch on.
583570
private func turnTorchOn() {
584571
guard let device = self.device else {

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: mobile_scanner
22
description: A universal Flutter barcode and QR code scanner using CameraX/ML Kit for Android, AVFoundation/Apple Vision for iOS & macOS, and ZXing for web.
3-
version: 7.1.3
3+
version: 7.1.4
44
repository: https://github.com/juliansteenbakker/mobile_scanner
55

66
screenshots:

0 commit comments

Comments
 (0)