diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 767f9a75e0a2..d519ed410ec6 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -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. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m index 65f19cdc7b7d..9490232e064b 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -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"]; }); @@ -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 diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m index 6cfe47b86fb0..45a993e9d443 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m @@ -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); diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index c00b8d68df03..fbf5e3b3a76b 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -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