Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[camera_avfoundation] do not append sample buffer when readyForMoreMediaData is NO to avoid crash #6901

Merged
merged 6 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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.17+1

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

## 0.9.17

* Adds Swift Package Manager compatibility.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,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 Down Expand Up @@ -200,4 +199,56 @@ - (void)testDidOutputSampleBufferSampleTimesMustBeNumericAfterPauseResume {
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
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,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.17
version: 0.9.17+1

environment:
sdk: ^3.2.3
Expand Down