diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index b8d61cb0b33..dae480a6b16 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.9.13+11 -* Remove development team from example app. +* Fixes a memory leak of sample buffer when pause and resume the video recording. +* Removes development team from example app. * Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. ## 0.9.13+10 diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m index 6f0a4edab08..24e35449114 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -38,6 +38,41 @@ - (void)testCopyPixelBuffer { CFRelease(deliveriedPixelBuffer); } +- (void)testDidOutputSampleBuffer_mustNotChangeSampleBufferRetainCountAfterPauseResumeRecording { + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("test", NULL)); + CMSampleBufferRef sampleBuffer = FLTCreateTestSampleBuffer(); + + 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 AVAssetWriterStatus status = AVAssetWriterStatusUnknown; + OCMStub([writerMock startWriting]).andDo(^(NSInvocation *invocation) { + status = AVAssetWriterStatusWriting; + }); + OCMStub([writerMock status]).andDo(^(NSInvocation *invocation) { + [invocation setReturnValue:&status]; + }); + + FLTThreadSafeFlutterResult *result = + [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id result){ + // no-op + }]; + + // Pause then resume the recording. + [cam startVideoRecordingWithResult:result]; + [cam pauseVideoRecordingWithResult:result]; + [cam resumeVideoRecordingWithResult:result]; + + [cam captureOutput:cam.captureVideoOutput + didOutputSampleBuffer:sampleBuffer + fromConnection:OCMClassMock([AVCaptureConnection class])]; + XCTAssertEqual(CFGetRetainCount(sampleBuffer), 1, + @"didOutputSampleBuffer must not change the sample buffer retain count after " + @"pause resume recording."); + CFRelease(sampleBuffer); +} + - (void)testDidOutputSampleBufferIgnoreAudioSamplesBeforeVideoSamples { FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("testing", NULL)); CMSampleBufferRef videoSample = FLTCreateTestSampleBuffer(); diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 1f940403320..b3dd9c53e2b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -514,7 +514,6 @@ - (void)captureOutput:(AVCaptureOutput *)output return; } - CFRetain(sampleBuffer); CMTime currentSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); if (_videoWriter.status != AVAssetWriterStatusWriting) { @@ -564,18 +563,18 @@ - (void)captureOutput:(AVCaptureOutput *)output _lastAudioSampleTime = currentSampleTime; if (_audioTimeOffset.value != 0) { - CFRelease(sampleBuffer); - sampleBuffer = [self adjustTime:sampleBuffer by:_audioTimeOffset]; + CMSampleBufferRef adjustedSampleBuffer = + [self copySampleBufferWithAdjustedTime:sampleBuffer by:_audioTimeOffset]; + [self newAudioSample:adjustedSampleBuffer]; + CFRelease(adjustedSampleBuffer); + } else { + [self newAudioSample:sampleBuffer]; } - - [self newAudioSample:sampleBuffer]; } - - CFRelease(sampleBuffer); } } -- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset CF_RETURNS_RETAINED { +- (CMSampleBufferRef)copySampleBufferWithAdjustedTime:(CMSampleBufferRef)sample by:(CMTime)offset { CMItemCount count; CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &count); CMSampleTimingInfo *pInfo = malloc(sizeof(CMSampleTimingInfo) * count); diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 175213883b1..e3845c4f5ea 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.13+10 +version: 0.9.13+11 environment: sdk: ^3.2.3