Skip to content

Commit 8e6ac90

Browse files
authored
[camera_avfoundation] fix sample times not being numeric after pause/resume. (flutter#6897)
Correctly initialise `_lastVideoSampleTime` and `_lastAudioSampleTime` before first use to avoid propagating invalid (non-numeric) values into methods like `appendPixelBuffer`. Fixes flutter#132014
1 parent 5cc6418 commit 8e6ac90

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
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.16+1
2+
3+
* Fixes sample times not being numeric after pause/resume.
4+
15
## 0.9.16
26

37
* Converts Dart-to-host communcation to Pigeon.

packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,72 @@ - (void)testDidOutputSampleBufferIgnoreAudioSamplesBeforeVideoSamples {
130130
CFRelease(audioSample);
131131
}
132132

133+
- (void)testDidOutputSampleBufferSampleTimesMustBeNumericAfterPauseResume {
134+
FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("testing", NULL));
135+
CMSampleBufferRef videoSample = FLTCreateTestSampleBuffer();
136+
CMSampleBufferRef audioSample = FLTCreateTestAudioSampleBuffer();
137+
138+
id connectionMock = OCMClassMock([AVCaptureConnection class]);
139+
140+
id writerMock = OCMClassMock([AVAssetWriter class]);
141+
OCMStub([writerMock alloc]).andReturn(writerMock);
142+
OCMStub([writerMock initWithURL:OCMOCK_ANY fileType:OCMOCK_ANY error:[OCMArg setTo:nil]])
143+
.andReturn(writerMock);
144+
__block AVAssetWriterStatus status = AVAssetWriterStatusUnknown;
145+
OCMStub([writerMock startWriting]).andDo(^(NSInvocation *invocation) {
146+
status = AVAssetWriterStatusWriting;
147+
});
148+
OCMStub([writerMock status]).andDo(^(NSInvocation *invocation) {
149+
[invocation setReturnValue:&status];
150+
});
151+
152+
__block BOOL videoAppended = NO;
153+
id adaptorMock = OCMClassMock([AVAssetWriterInputPixelBufferAdaptor class]);
154+
OCMStub([adaptorMock assetWriterInputPixelBufferAdaptorWithAssetWriterInput:OCMOCK_ANY
155+
sourcePixelBufferAttributes:OCMOCK_ANY])
156+
.andReturn(adaptorMock);
157+
OCMStub([adaptorMock appendPixelBuffer:[OCMArg anyPointer] withPresentationTime:kCMTimeZero])
158+
.ignoringNonObjectArgs()
159+
.andDo(^(NSInvocation *invocation) {
160+
CMTime presentationTime;
161+
[invocation getArgument:&presentationTime atIndex:3];
162+
XCTAssert(CMTIME_IS_NUMERIC(presentationTime));
163+
videoAppended = YES;
164+
});
165+
166+
__block BOOL audioAppended = NO;
167+
id inputMock = OCMClassMock([AVAssetWriterInput class]);
168+
OCMStub([inputMock assetWriterInputWithMediaType:OCMOCK_ANY outputSettings:OCMOCK_ANY])
169+
.andReturn(inputMock);
170+
OCMStub([inputMock isReadyForMoreMediaData]).andReturn(YES);
171+
OCMStub([inputMock appendSampleBuffer:[OCMArg anyPointer]]).andDo(^(NSInvocation *invocation) {
172+
CMSampleBufferRef sampleBuffer;
173+
[invocation getArgument:&sampleBuffer atIndex:2];
174+
CMTime sampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
175+
XCTAssert(CMTIME_IS_NUMERIC(sampleTime));
176+
audioAppended = YES;
177+
});
178+
179+
[cam
180+
startVideoRecordingWithCompletion:^(FlutterError *_Nullable error) {
181+
}
182+
messengerForStreaming:nil];
183+
184+
[cam pauseVideoRecording];
185+
[cam resumeVideoRecording];
186+
187+
[cam captureOutput:cam.captureVideoOutput
188+
didOutputSampleBuffer:videoSample
189+
fromConnection:connectionMock];
190+
[cam captureOutput:nil didOutputSampleBuffer:audioSample fromConnection:connectionMock];
191+
[cam captureOutput:cam.captureVideoOutput
192+
didOutputSampleBuffer:videoSample
193+
fromConnection:connectionMock];
194+
[cam captureOutput:nil didOutputSampleBuffer:audioSample fromConnection:connectionMock];
195+
XCTAssert(videoAppended && audioAppended, @"Video or audio was not appended.");
196+
197+
CFRelease(videoSample);
198+
CFRelease(audioSample);
199+
}
200+
133201
@end

packages/camera/camera_avfoundation/ios/Classes/FLTCam.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,10 @@ - (void)captureOutput:(AVCaptureOutput *)output
672672
if (_videoWriter.status != AVAssetWriterStatusWriting) {
673673
[_videoWriter startWriting];
674674
[_videoWriter startSessionAtSourceTime:currentSampleTime];
675+
// fix sample times not being numeric when pause/resume happens before first sample buffer
676+
// arrives https://github.com/flutter/flutter/issues/132014
677+
_lastVideoSampleTime = currentSampleTime;
678+
_lastAudioSampleTime = currentSampleTime;
675679
}
676680

677681
if (output == _captureVideoOutput) {

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.16
5+
version: 0.9.16+1
66

77
environment:
88
sdk: ^3.2.3

0 commit comments

Comments
 (0)