Skip to content

Commit fdf7512

Browse files
committed
Ignore non image sample buffers for streaming
1 parent 70a13a8 commit fdf7512

File tree

4 files changed

+94
-61
lines changed

4 files changed

+94
-61
lines changed

packages/camera/camera_avfoundation/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.9.21
2+
3+
* Fixes crash when streaming is enabled during recording.
4+
15
## 0.9.20+7
26

37
* Updates kotlin version to 2.2.0 to enable gradle 8.11 support.

packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ final class StreamingTests: XCTestCase {
3636
DefaultCamera,
3737
AVCaptureOutput,
3838
CMSampleBuffer,
39+
CMSampleBuffer,
3940
AVCaptureConnection
4041
) {
4142
let captureSessionQueue = DispatchQueue(label: "testing")
@@ -45,13 +46,14 @@ final class StreamingTests: XCTestCase {
4546
let camera = CameraTestUtils.createTestCamera(configuration)
4647
let testAudioOutput = CameraTestUtils.createTestAudioOutput()
4748
let sampleBuffer = CameraTestUtils.createTestSampleBuffer()
49+
let audioSampleBuffer = CameraTestUtils.createTestAudioSampleBuffer()
4850
let testAudioConnection = CameraTestUtils.createTestConnection(testAudioOutput)
4951

50-
return (camera, testAudioOutput, sampleBuffer, testAudioConnection)
52+
return (camera, testAudioOutput, sampleBuffer, audioSampleBuffer, testAudioConnection)
5153
}
5254

5355
func testExceedMaxStreamingPendingFramesCount() {
54-
let (camera, testAudioOutput, sampleBuffer, testAudioConnection) = createCamera()
56+
let (camera, testAudioOutput, sampleBuffer, _, testAudioConnection) = createCamera()
5557
let handlerMock = MockImageStreamHandler()
5658

5759
let finishStartStreamExpectation = expectation(
@@ -87,7 +89,7 @@ final class StreamingTests: XCTestCase {
8789
}
8890

8991
func testReceivedImageStreamData() {
90-
let (camera, testAudioOutput, sampleBuffer, testAudioConnection) = createCamera()
92+
let (camera, testAudioOutput, sampleBuffer, _, testAudioConnection) = createCamera()
9193
let handlerMock = MockImageStreamHandler()
9294

9395
let finishStartStreamExpectation = expectation(
@@ -124,8 +126,34 @@ final class StreamingTests: XCTestCase {
124126
waitForExpectations(timeout: 30, handler: nil)
125127
}
126128

129+
func testIgnoresNonImageBuffers() {
130+
let (camera, testAudioOutput, _, audioSampleBuffer, testAudioConnection) = createCamera()
131+
let handlerMock = MockImageStreamHandler()
132+
handlerMock.eventSinkStub = { event in
133+
XCTFail()
134+
}
135+
136+
let finishStartStreamExpectation = expectation(
137+
description: "Finish startStream")
138+
139+
let messenger = MockFlutterBinaryMessenger()
140+
camera.startImageStream(
141+
with: messenger, imageStreamHandler: handlerMock,
142+
completion: {
143+
_ in
144+
finishStartStreamExpectation.fulfill()
145+
})
146+
147+
waitForExpectations(timeout: 30, handler: nil)
148+
XCTAssertEqual(camera.isStreamingImages, true)
149+
150+
camera.captureOutput(testAudioOutput, didOutput: audioSampleBuffer, from: testAudioConnection)
151+
152+
waitForQueueRoundTrip(with: DispatchQueue.main)
153+
}
154+
127155
func testImageStreamEventFormat() {
128-
let (camera, testAudioOutput, sampleBuffer, testAudioConnection) = createCamera()
156+
let (camera, testAudioOutput, sampleBuffer, _, testAudioConnection) = createCamera()
129157

130158
let expectation = expectation(description: "Received a valid event")
131159

packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -762,66 +762,67 @@ final class DefaultCamera: FLTCam, Camera {
762762
if let eventSink = imageStreamHandler?.eventSink,
763763
streamingPendingFramesCount < maxStreamingPendingFramesCount
764764
{
765-
streamingPendingFramesCount += 1
766-
767-
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
768-
// Must lock base address before accessing the pixel data
769-
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
770-
771-
let imageWidth = CVPixelBufferGetWidth(pixelBuffer)
772-
let imageHeight = CVPixelBufferGetHeight(pixelBuffer)
773-
774-
var planes: [[String: Any]] = []
775-
776-
let isPlanar = CVPixelBufferIsPlanar(pixelBuffer)
777-
let planeCount = isPlanar ? CVPixelBufferGetPlaneCount(pixelBuffer) : 1
778-
779-
for i in 0..<planeCount {
780-
let planeAddress: UnsafeMutableRawPointer?
781-
let bytesPerRow: Int
782-
let height: Int
783-
let width: Int
784-
785-
if isPlanar {
786-
planeAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, i)
787-
bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, i)
788-
height = CVPixelBufferGetHeightOfPlane(pixelBuffer, i)
789-
width = CVPixelBufferGetWidthOfPlane(pixelBuffer, i)
790-
} else {
791-
planeAddress = CVPixelBufferGetBaseAddress(pixelBuffer)
792-
bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
793-
height = CVPixelBufferGetHeight(pixelBuffer)
794-
width = CVPixelBufferGetWidth(pixelBuffer)
765+
if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
766+
streamingPendingFramesCount += 1
767+
768+
// Must lock base address before accessing the pixel data
769+
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
770+
771+
let imageWidth = CVPixelBufferGetWidth(pixelBuffer)
772+
let imageHeight = CVPixelBufferGetHeight(pixelBuffer)
773+
774+
var planes: [[String: Any]] = []
775+
776+
let isPlanar = CVPixelBufferIsPlanar(pixelBuffer)
777+
let planeCount = isPlanar ? CVPixelBufferGetPlaneCount(pixelBuffer) : 1
778+
779+
for i in 0..<planeCount {
780+
let planeAddress: UnsafeMutableRawPointer?
781+
let bytesPerRow: Int
782+
let height: Int
783+
let width: Int
784+
785+
if isPlanar {
786+
planeAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, i)
787+
bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, i)
788+
height = CVPixelBufferGetHeightOfPlane(pixelBuffer, i)
789+
width = CVPixelBufferGetWidthOfPlane(pixelBuffer, i)
790+
} else {
791+
planeAddress = CVPixelBufferGetBaseAddress(pixelBuffer)
792+
bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
793+
height = CVPixelBufferGetHeight(pixelBuffer)
794+
width = CVPixelBufferGetWidth(pixelBuffer)
795+
}
796+
797+
let length = bytesPerRow * height
798+
let bytes = Data(bytes: planeAddress!, count: length)
799+
800+
let planeBuffer: [String: Any] = [
801+
"bytesPerRow": bytesPerRow,
802+
"width": width,
803+
"height": height,
804+
"bytes": FlutterStandardTypedData(bytes: bytes),
805+
]
806+
planes.append(planeBuffer)
795807
}
796808

797-
let length = bytesPerRow * height
798-
let bytes = Data(bytes: planeAddress!, count: length)
799-
800-
let planeBuffer: [String: Any] = [
801-
"bytesPerRow": bytesPerRow,
802-
"width": width,
803-
"height": height,
804-
"bytes": FlutterStandardTypedData(bytes: bytes),
809+
// Lock the base address before accessing pixel data, and unlock it afterwards.
810+
// Done accessing the `pixelBuffer` at this point.
811+
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
812+
813+
let imageBuffer: [String: Any] = [
814+
"width": imageWidth,
815+
"height": imageHeight,
816+
"format": videoFormat,
817+
"planes": planes,
818+
"lensAperture": Double(captureDevice.lensAperture()),
819+
"sensorExposureTime": Int(captureDevice.exposureDuration().seconds * 1_000_000_000),
820+
"sensorSensitivity": Double(captureDevice.iso()),
805821
]
806-
planes.append(planeBuffer)
807-
}
808822

809-
// Lock the base address before accessing pixel data, and unlock it afterwards.
810-
// Done accessing the `pixelBuffer` at this point.
811-
CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
812-
813-
let imageBuffer: [String: Any] = [
814-
"width": imageWidth,
815-
"height": imageHeight,
816-
"format": videoFormat,
817-
"planes": planes,
818-
"lensAperture": Double(captureDevice.lensAperture()),
819-
"sensorExposureTime": Int(captureDevice.exposureDuration().seconds * 1_000_000_000),
820-
"sensorSensitivity": Double(captureDevice.iso()),
821-
]
822-
823-
DispatchQueue.main.async {
824-
eventSink(imageBuffer)
823+
DispatchQueue.main.async {
824+
eventSink(imageBuffer)
825+
}
825826
}
826827
}
827828
}

packages/camera/camera_avfoundation/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: camera_avfoundation
22
description: iOS implementation of the camera plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
5-
version: 0.9.20+7
5+
version: 0.9.21
66

77
environment:
88
sdk: ^3.6.0

0 commit comments

Comments
 (0)