xref: /aosp_15_r20/external/webrtc/sdk/objc/unittests/avformatmappertests.mm (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1/*
2 *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#import <Foundation/Foundation.h>
12#import <OCMock/OCMock.h>
13
14#include "rtc_base/gunit.h"
15
16// Width and height don't play any role so lets use predefined values throughout
17// the tests.
18static const int kFormatWidth = 789;
19static const int kFormatHeight = 987;
20
21// Hardcoded framrate to be used throughout the tests.
22static const int kFramerate = 30;
23
24// Same width and height is used so it's ok to expect same cricket::VideoFormat
25static cricket::VideoFormat expectedFormat =
26    cricket::VideoFormat(kFormatWidth,
27                         kFormatHeight,
28                         cricket::VideoFormat::FpsToInterval(kFramerate),
29                         cricket::FOURCC_NV12);
30
31// Mock class for AVCaptureDeviceFormat.
32// Custom implementation needed because OCMock cannot handle the
33// CMVideoDescriptionRef mocking.
34@interface AVCaptureDeviceFormatMock : NSObject
35
36@property (nonatomic, assign) CMVideoFormatDescriptionRef format;
37@property (nonatomic, strong) OCMockObject *rangeMock;
38
39- (instancetype)initWithMediaSubtype:(FourCharCode)subtype
40                              minFps:(float)minFps
41                              maxFps:(float)maxFps;
42+ (instancetype)validFormat;
43+ (instancetype)invalidFpsFormat;
44+ (instancetype)invalidMediaSubtypeFormat;
45
46@end
47
48@implementation AVCaptureDeviceFormatMock
49
50@synthesize format = _format;
51@synthesize rangeMock = _rangeMock;
52
53- (instancetype)initWithMediaSubtype:(FourCharCode)subtype
54                              minFps:(float)minFps
55                              maxFps:(float)maxFps {
56  if (self = [super init]) {
57    CMVideoFormatDescriptionCreate(nil, subtype, kFormatWidth, kFormatHeight,
58                                   nil, &_format);
59    // We can use OCMock for the range.
60    _rangeMock = [OCMockObject mockForClass:[AVFrameRateRange class]];
61    [[[_rangeMock stub] andReturnValue:@(minFps)] minFrameRate];
62    [[[_rangeMock stub] andReturnValue:@(maxFps)] maxFrameRate];
63  }
64
65  return self;
66}
67
68+ (instancetype)validFormat {
69  AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc]
70      initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
71                    minFps:0.0
72                    maxFps:30.0];
73  return instance;
74}
75
76+ (instancetype)invalidFpsFormat {
77  AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc]
78      initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
79                    minFps:0.0
80                    maxFps:22.0];
81  return instance;
82}
83
84+ (instancetype)invalidMediaSubtypeFormat {
85  AVCaptureDeviceFormatMock *instance = [[AVCaptureDeviceFormatMock alloc]
86      initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8Planar
87                    minFps:0.0
88                    maxFps:60.0];
89  return instance;
90}
91
92- (void)dealloc {
93  if (_format != nil) {
94    CFRelease(_format);
95    _format = nil;
96  }
97}
98
99// Redefinition of AVCaptureDevice methods we want to mock.
100- (CMVideoFormatDescriptionRef)formatDescription {
101  return self.format;
102}
103
104- (NSArray *)videoSupportedFrameRateRanges {
105  return @[ self.rangeMock ];
106}
107
108@end
109
110TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFramerateFormats) {
111  // given
112  id mockDevice = OCMClassMock([AVCaptureDevice class]);
113
114  // Valid media subtype, invalid framerate
115  AVCaptureDeviceFormatMock* mock =
116      [AVCaptureDeviceFormatMock invalidFpsFormat];
117  OCMStub([mockDevice formats]).andReturn(@[ mock ]);
118
119  // when
120  std::set<cricket::VideoFormat> result =
121      webrtc::GetSupportedVideoFormatsForDevice(mockDevice);
122
123  // then
124  EXPECT_TRUE(result.empty());
125}
126
127TEST(AVFormatMapperTest, SuportedCricketFormatsWithInvalidFormats) {
128  // given
129  id mockDevice = OCMClassMock([AVCaptureDevice class]);
130
131  // Invalid media subtype, valid framerate
132  AVCaptureDeviceFormatMock* mock =
133      [AVCaptureDeviceFormatMock invalidMediaSubtypeFormat];
134  OCMStub([mockDevice formats]).andReturn(@[ mock ]);
135
136  // when
137  std::set<cricket::VideoFormat> result =
138      webrtc::GetSupportedVideoFormatsForDevice(mockDevice);
139
140  // then
141  EXPECT_TRUE(result.empty());
142}
143
144TEST(AVFormatMapperTest, SuportedCricketFormats) {
145  // given
146  id mockDevice = OCMClassMock([AVCaptureDevice class]);
147
148  // valid media subtype, valid framerate
149  AVCaptureDeviceFormatMock* mock = [AVCaptureDeviceFormatMock validFormat];
150  OCMStub([mockDevice formats]).andReturn(@[ mock ]);
151
152  // when
153  std::set<cricket::VideoFormat> result =
154      webrtc::GetSupportedVideoFormatsForDevice(mockDevice);
155
156  // then
157  EXPECT_EQ(1u, result.size());
158  // make sure the set has the expected format
159  EXPECT_EQ(expectedFormat, *result.begin());
160}
161
162TEST(AVFormatMapperTest, MediaSubtypePreference) {
163  // given
164  id mockDevice = OCMClassMock([AVCaptureDevice class]);
165
166  // valid media subtype, valid framerate
167  AVCaptureDeviceFormatMock* mockOne = [[AVCaptureDeviceFormatMock alloc]
168      initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
169                    minFps:0.0
170                    maxFps:30.0];
171  // valid media subtype, valid framerate.
172  // This media subtype should be the preffered one.
173  AVCaptureDeviceFormatMock* mockTwo = [[AVCaptureDeviceFormatMock alloc]
174      initWithMediaSubtype:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
175                    minFps:0.0
176                    maxFps:30.0];
177  OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES);
178  OCMStub([mockDevice unlockForConfiguration]);
179  NSArray* array = @[ mockOne, mockTwo ];
180  OCMStub([mockDevice formats]).andReturn(array);
181
182  // to verify
183  OCMExpect([mockDevice setActiveFormat:(AVCaptureDeviceFormat*)mockTwo]);
184  OCMExpect(
185      [mockDevice setActiveVideoMinFrameDuration:CMTimeMake(1, kFramerate)]);
186
187  // when
188  bool resultFormat =
189      webrtc::SetFormatForCaptureDevice(mockDevice, nil, expectedFormat);
190
191  // then
192  EXPECT_TRUE(resultFormat);
193  [mockDevice verify];
194}
195
196TEST(AVFormatMapperTest, SetFormatWhenDeviceCannotLock) {
197  // given
198  id mockDevice = OCMClassMock([AVCaptureDevice class]);
199  [[[mockDevice stub] andReturnValue:@(NO)]
200      lockForConfiguration:[OCMArg setTo:nil]];
201  [[[mockDevice stub] andReturn:@[]] formats];
202
203  // when
204  bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil,
205                                                        cricket::VideoFormat());
206
207  // then
208  EXPECT_FALSE(resultFormat);
209}
210
211TEST(AVFormatMapperTest, SetFormatWhenFormatIsIncompatible) {
212  // given
213  id mockDevice = OCMClassMock([AVCaptureDevice class]);
214  OCMStub([mockDevice formats]).andReturn(@[]);
215  OCMStub([mockDevice lockForConfiguration:[OCMArg setTo:nil]]).andReturn(YES);
216  NSException* testException =
217      [NSException exceptionWithName:@"Test exception"
218                              reason:@"Raised from unit tests"
219                            userInfo:nil];
220  OCMStub([mockDevice setActiveFormat:[OCMArg any]]).andThrow(testException);
221  OCMExpect([mockDevice unlockForConfiguration]);
222
223  // when
224  bool resultFormat = webrtc::SetFormatForCaptureDevice(mockDevice, nil,
225                                                        cricket::VideoFormat());
226
227  // then
228  EXPECT_FALSE(resultFormat);
229
230  // TODO(denicija): Remove try-catch when Chromium rolls this change:
231  // https://github.com/erikdoe/ocmock/commit/de1419415581dc307045e54bfe9c98c86efea96b
232  // Without it, stubbed exceptions are being re-raised on [mock verify].
233  // More information here:
234  //https://github.com/erikdoe/ocmock/issues/241
235  @try {
236    [mockDevice verify];
237  } @catch (NSException* exception) {
238    if ([exception.reason isEqual:testException.reason]) {
239      // Nothing dangerous here
240      EXPECT_TRUE([exception.reason isEqualToString:exception.reason]);
241    }
242  }
243}
244