Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions packages/camera/camera_avfoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.16+1

* Fixes a crash due to appending sample buffers when readyForMoreMediaData is NO

## 0.9.16

* Converts Dart-to-host communcation to Pigeon.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,21 @@ - (void)testDidOutputSampleBufferIgnoreAudioSamplesBeforeVideoSamples {

__block NSArray *writtenSamples = @[];

id videoMock = OCMClassMock([AVAssetWriterInputPixelBufferAdaptor class]);
OCMStub([videoMock assetWriterInputPixelBufferAdaptorWithAssetWriterInput:OCMOCK_ANY
sourcePixelBufferAttributes:OCMOCK_ANY])
.andReturn(videoMock);
OCMStub([videoMock appendPixelBuffer:[OCMArg anyPointer] withPresentationTime:kCMTimeZero])
id adaptorMock = OCMClassMock([AVAssetWriterInputPixelBufferAdaptor class]);
OCMStub([adaptorMock assetWriterInputPixelBufferAdaptorWithAssetWriterInput:OCMOCK_ANY
sourcePixelBufferAttributes:OCMOCK_ANY])
.andReturn(adaptorMock);
OCMStub([adaptorMock appendPixelBuffer:[OCMArg anyPointer] withPresentationTime:kCMTimeZero])
.ignoringNonObjectArgs()
.andDo(^(NSInvocation *invocation) {
writtenSamples = [writtenSamples arrayByAddingObject:@"video"];
});

id audioMock = OCMClassMock([AVAssetWriterInput class]);
OCMStub([audioMock assetWriterInputWithMediaType:[OCMArg isEqual:AVMediaTypeAudio]
outputSettings:OCMOCK_ANY])
.andReturn(audioMock);
OCMStub([audioMock isReadyForMoreMediaData]).andReturn(YES);
OCMStub([audioMock appendSampleBuffer:[OCMArg anyPointer]]).andDo(^(NSInvocation *invocation) {
id inputMock = OCMClassMock([AVAssetWriterInput class]);
OCMStub([inputMock assetWriterInputWithMediaType:OCMOCK_ANY outputSettings:OCMOCK_ANY])
.andReturn(inputMock);
OCMStub([inputMock isReadyForMoreMediaData]).andReturn(YES);
OCMStub([inputMock appendSampleBuffer:[OCMArg anyPointer]]).andDo(^(NSInvocation *invocation) {
writtenSamples = [writtenSamples arrayByAddingObject:@"audio"];
});

Expand All @@ -130,4 +129,56 @@ - (void)testDidOutputSampleBufferIgnoreAudioSamplesBeforeVideoSamples {
CFRelease(audioSample);
}

- (void)testDidOutputSampleBufferMustNotAppendSampleWhenReadyForMoreMediaDataIsNo {
FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("testing", NULL));
CMSampleBufferRef videoSample = FLTCreateTestSampleBuffer();

id connectionMock = OCMClassMock([AVCaptureConnection class]);

id writerMock = OCMClassMock([AVAssetWriter class]);
OCMStub([writerMock alloc]).andReturn(writerMock);
OCMStub([writerMock initWithURL:OCMOCK_ANY fileType:OCMOCK_ANY error:[OCMArg setTo:nil]])
.andReturn(writerMock);

__block BOOL sampleAppended = NO;
id adaptorMock = OCMClassMock([AVAssetWriterInputPixelBufferAdaptor class]);
OCMStub([adaptorMock assetWriterInputPixelBufferAdaptorWithAssetWriterInput:OCMOCK_ANY
sourcePixelBufferAttributes:OCMOCK_ANY])
.andReturn(adaptorMock);
OCMStub([adaptorMock appendPixelBuffer:[OCMArg anyPointer] withPresentationTime:kCMTimeZero])
.ignoringNonObjectArgs()
.andDo(^(NSInvocation *invocation) {
sampleAppended = YES;
});

__block BOOL readyForMoreMediaData = NO;
id inputMock = OCMClassMock([AVAssetWriterInput class]);
OCMStub([inputMock assetWriterInputWithMediaType:OCMOCK_ANY outputSettings:OCMOCK_ANY])
.andReturn(inputMock);
OCMStub([inputMock isReadyForMoreMediaData]).andDo(^(NSInvocation *invocation) {
[invocation setReturnValue:&readyForMoreMediaData];
});

[cam
startVideoRecordingWithCompletion:^(FlutterError *_Nullable error) {
}
messengerForStreaming:nil];

readyForMoreMediaData = YES;
sampleAppended = NO;
[cam captureOutput:cam.captureVideoOutput
didOutputSampleBuffer:videoSample
fromConnection:connectionMock];
XCTAssertTrue(sampleAppended, @"Sample was not appended.");

readyForMoreMediaData = NO;
sampleAppended = NO;
[cam captureOutput:cam.captureVideoOutput
didOutputSampleBuffer:videoSample
fromConnection:connectionMock];
XCTAssertFalse(sampleAppended, @"Sample cannot be appended when readyForMoreMediaData is NO.");

CFRelease(videoSample);
}

@end
6 changes: 5 additions & 1 deletion packages/camera/camera_avfoundation/ios/Classes/FLTCam.m
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,11 @@ - (void)captureOutput:(AVCaptureOutput *)output

CVPixelBufferRef nextBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CMTime nextSampleTime = CMTimeSubtract(_lastVideoSampleTime, _videoTimeOffset);
[_videoAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextSampleTime];
// do not append sample buffer when readyForMoreMediaData is NO to avoid crash
// https://github.com/flutter/flutter/issues/132073
if (_videoWriterInput.readyForMoreMediaData) {
[_videoAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextSampleTime];
}
} else {
CMTime dur = CMSampleBufferGetDuration(sampleBuffer);

Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_avfoundation/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_avfoundation
description: iOS implementation of the camera plugin.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.9.16
version: 0.9.16+1

environment:
sdk: ^3.2.3
Expand Down