Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
01ad812
Set higher resolution when using max preset
sergeidesenko Jul 30, 2023
5f5cabc
Merge remote-tracking branch 'upstream/main' into camera_avfoundation…
sergeidesenko Oct 27, 2023
f9f302f
chore: update changelog
sergeidesenko Oct 27, 2023
e5a33f6
chore: update changelog and pubspec
sergeidesenko Oct 27, 2023
d3ce411
refactor: add comments
sergeidesenko Oct 27, 2023
5813f25
chore: update pubspec
sergeidesenko Oct 27, 2023
60e043a
feat(tests): add convenience method for camera init
sergeidesenko Jan 22, 2024
9830404
feat(tests): add tests for setting resolutionPresets
sergeidesenko Jan 22, 2024
87b0c59
chore(tests): add tests file to .pbxproj
sergeidesenko Jan 22, 2024
0c29019
chore: fix formatting for updated files
sergeidesenko Jan 22, 2024
ae0210f
fix: don't update the changelog
sergeidesenko Jan 31, 2024
0c8b21a
refactor: remove todo and renaming
sergeidesenko Jan 31, 2024
37ffaa7
Merge remote-tracking branch 'upstream/main' into camera_avfoundation…
sergeidesenko Jan 31, 2024
70deac4
Merge branch 'main' into camera_avfoundation-max-resolution
sergeidesenko Feb 2, 2024
0fe1ac7
feat: add tests for different resolution presets
sergeidesenko Feb 2, 2024
c314755
refactor: update comment
sergeidesenko Feb 2, 2024
ec1b896
refactor: change tests to use DI
sergeidesenko Feb 2, 2024
5e6f1f5
refactor: rename method
sergeidesenko Feb 2, 2024
decce1e
refactor: rename method
sergeidesenko Feb 2, 2024
832cc7b
refactor: cleaner DI
sergeidesenko Feb 13, 2024
ddfe778
refactor: update comments
sergeidesenko Feb 21, 2024
5c01c8c
refactor: update comment to match arguments
sergeidesenko Feb 21, 2024
0c40901
refactor: rename types
sergeidesenko Feb 22, 2024
42e9cfa
Merge branch 'main' into camera_avfoundation-max-resolution
sergeidesenko Mar 1, 2024
8e019b3
refactor: address code review
sergeidesenko Mar 1, 2024
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
Prev Previous commit
Next Next commit
chore: fix formatting for updated files
  • Loading branch information
sergeidesenko committed Jan 22, 2024
commit 0c29019125242862fd73e8ab4ce282c2bfe04e82
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this file should be ...Tests.m, not ...Test.m, to follow our standard convention.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,29 @@ - (void)testResolutionPresetWithCanSetSessionPresetMax_mustUpdateCaptureSessionP
OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op
OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);
OCMExpect([videoSessionMock setSessionPreset:[OCMArg checkWithBlock:^BOOL(id value) {
// Return YES if the value is one of the expected presets
return [value isEqualToString:expectedUltraHighPreset] || [value isEqualToString:expectedMaxPreset];
}]]);
// Return YES if the value is one of the expected presets
return [value isEqualToString:expectedUltraHighPreset] ||
[value isEqualToString:expectedMaxPreset];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explicitly mock the values and verify each of them?

Copy link
Contributor Author

@sergeidesenko sergeidesenko Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!
I'm not sure I understood your message here. If you are suggesting two different tests (one for expectedUltraHighPreset and one for expectedMaxPreset), then that's not really the expected behaviour.
The test is for the case where camera receives FLTResolutionPresetMax, and in that case sessionPreset can be one of these two values and both are acceptable.
If I missed your point - could you please clarify what you meant?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in that case sessionPreset can be one of these two values and both are acceptable.

Can you explain when will it be one value and when the other value? It's unclear which one will be true when running the test.

Copy link
Contributor Author

@sergeidesenko sergeidesenko Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you take a look in the file FLTCam.m at line 372, there are two options on how to handle FLTResolutionPresetMax. Either we can find bestFormat, in which case sessionPreset is set to AVCaptureSessionPresetInputPriority, or the bestFormat is nil, in which case we try to set sessionPreset to AVCaptureSessionPreset3840x2160.

Since method canSetSessionPreset of our mock object always returns true, we can expect that if we try to set AVCaptureSessionPreset3840x2160, we will always succeed. That's how we get only to possible outcomes.

It's unclear which one will be true when running the test.

About that - we could mock further and provide our videoSessionMock with availableFormats, split the test in two different tests - one that covers case with the best format and one that covers case with no formats and AVCaptureSessionPreset3840x2160. But since both of those cases are an acceptable response to setting FLTResolutionPresetMax (as I explained above), I didn't see the need to go this far

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since method canSetSessionPreset of our mock object always returns true, we can expect that if we try to set AVCaptureSessionPreset3840x2160, we will always succeed. That's how we get only to possible outcomes.

If we have two codepaths, we should be testing both of them.

But since both of those cases are an acceptable response to setting FLTResolutionPresetMax (as I explained above), I didn't see the need to go this far

Why would we want no test coverage that one of the possible outcomes actually works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have two codepaths, we should be testing both of them.

Thank you! I'll add another test

Why would we want no test coverage that one of the possible outcomes actually works?

I'm not sure, since this whole behavior of resolution presets wasn't covered by any test at all :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}]]);

FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"max");


FLTCam *cam = FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"max");

OCMVerifyAll(videoSessionMock);
}

- (void)testResolutionPresetWithCanSetSessionPresetUltraHigh_mustUpdateCaptureSessionPreset { //TODO
- (void)
testResolutionPresetWithCanSetSessionPresetUltraHigh_mustUpdateCaptureSessionPreset { // TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

NSString *expectedPreset = AVCaptureSessionPreset3840x2160;

id videoSessionMock = OCMClassMock([AVCaptureSession class]);
OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a no-op line in the code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed no-op comments for this line, seems unnecessary

OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);

// expect that setting "ultraHigh" resolutionPreset correctly updates videoCaptureSession
OCMExpect([videoSessionMock setSessionPreset:expectedPreset]);
FLTCam *cam = FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"ultraHigh");

FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"ultraHigh");

OCMVerifyAll(videoSessionMock);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ extern FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessi
/// @param captureSession AVCaptureSession for video
/// @param resolutionPreset preset for camera's captureSession resolution
/// @return an FLTCam object.
extern FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession, NSString *resolutionPreset);
extern FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession,
NSString *resolutionPreset);

/// Creates a test sample buffer.
/// @return a test sample buffer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
error:nil];
}

FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession, NSString *resolutionPreset) {
FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession,
NSString *resolutionPreset) {
id inputMock = OCMClassMock([AVCaptureDeviceInput class]);
OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]])
.andReturn(inputMock);
Expand Down
60 changes: 29 additions & 31 deletions packages/camera/camera_avfoundation/ios/Classes/FLTCam.m
Original file line number Diff line number Diff line change
Expand Up @@ -347,25 +347,23 @@ - (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension

- (BOOL)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset withError:(NSError **)error {
switch (resolutionPreset) {
case FLTResolutionPresetMax:
{
AVCaptureDeviceFormat *bestFormat = [self getHighestResolutionFormatFor:_captureDevice];
if ( bestFormat ) {
_videoCaptureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
if ( [_captureDevice lockForConfiguration:NULL] == YES ) {

// set best device format and finish device configuration
_captureDevice.activeFormat = bestFormat;
[_captureDevice unlockForConfiguration];

// set preview size based on values from the current _captureDevice
_previewSize =
CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width,
_captureDevice.activeFormat.highResolutionStillImageDimensions.height);
break;
}
}
case FLTResolutionPresetMax: {
AVCaptureDeviceFormat *bestFormat = [self getHighestResolutionFormatFor:_captureDevice];
if (bestFormat) {
_videoCaptureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
if ([_captureDevice lockForConfiguration:NULL] == YES) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Never compare to YES; see go/objc-style#bool-expressions-and-conversions

// set best device format and finish device configuration
_captureDevice.activeFormat = bestFormat;
[_captureDevice unlockForConfiguration];

// set preview size based on values from the current _captureDevice
_previewSize =
CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width,
_captureDevice.activeFormat.highResolutionStillImageDimensions.height);
break;
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This should be after the code below, enclosing the entire case implementation, otherwise the indentation is very confusing.

Copy link
Contributor Author

@sergeidesenko sergeidesenko Mar 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed everything
In this case I decided to just remove everything related to AVCaptureSessionPreset3840x2160, since it was just a repetition of the next case (FLTResolutionPresetUltraHigh). Now it's more in line with other cases

Out of curiosity - what's your definition of nit in this context? I thought these are not compulsory

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally use it to indicate that something is a very small change. I wasn't aware there was a doc that equated it with being optional; I'll stop using it given that.

if ([_videoCaptureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) {
_videoCaptureSession.sessionPreset = AVCaptureSessionPreset3840x2160;
_previewSize = CGSizeMake(3840, 2160);
Expand Down Expand Up @@ -427,20 +425,20 @@ - (BOOL)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset withError:
}

/// Finds the highest available resolution in terms of pixel count for the given device
- (AVCaptureDeviceFormat *)getHighestResolutionFormatFor:(AVCaptureDevice*)captureDevice {
AVCaptureDeviceFormat *bestFormat = nil;
NSUInteger maxPixelCount = 0;
for ( AVCaptureDeviceFormat *format in [_captureDevice formats] ) {
CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
NSUInteger height = res.height;
NSUInteger width = res.width;
NSUInteger pixelCount = height * width;
if ( pixelCount > maxPixelCount ) {
maxPixelCount = pixelCount;
bestFormat = format;
}
- (AVCaptureDeviceFormat *)getHighestResolutionFormatFor:(AVCaptureDevice *)captureDevice {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: getHighestResolutionFormatForCaptureDevice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

AVCaptureDeviceFormat *bestFormat = nil;
NSUInteger maxPixelCount = 0;
for (AVCaptureDeviceFormat *format in [_captureDevice formats]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: in _captureDevice.formats

CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
NSUInteger height = res.height;
NSUInteger width = res.width;
NSUInteger pixelCount = height * width;
if (pixelCount > maxPixelCount) {
maxPixelCount = pixelCount;
bestFormat = format;
}
return bestFormat;
}
return bestFormat;
}

- (void)captureOutput:(AVCaptureOutput *)output
Expand Down