blob: 4c8bf348f441cf5ea976e8b3e911bfa9f5aeddf9 [file] [log] [blame]
Anders Carlssonfe9d8172018-04-03 11:40:39 +02001/*
2 * Copyright 2018 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 <XCTest/XCTest.h>
13
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020014#include "sdk/objc/native/src/objc_video_track_source.h"
Anders Carlssonfe9d8172018-04-03 11:40:39 +020015
Anders Carlsson4e5af962018-09-03 14:44:50 +020016#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020017#import "base/RTCVideoFrame.h"
18#import "base/RTCVideoFrameBuffer.h"
19#import "components/video_frame_buffer/RTCCVPixelBuffer.h"
20#import "frame_buffer_helpers.h"
21
Mirko Bonadeid9708072019-01-25 20:26:48 +010022#include "api/scoped_refptr.h"
Anders Carlssonfe9d8172018-04-03 11:40:39 +020023#include "common_video/libyuv/include/webrtc_libyuv.h"
Steve Anton10542f22019-01-11 09:11:00 -080024#include "media/base/fake_video_renderer.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020025#include "sdk/objc/native/api/video_frame.h"
Anders Carlssonfe9d8172018-04-03 11:40:39 +020026
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020027typedef void (^VideoSinkCallback)(RTC_OBJC_TYPE(RTCVideoFrame) *);
Anders Carlssonfe9d8172018-04-03 11:40:39 +020028
29namespace {
30
31class ObjCCallbackVideoSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
32 public:
33 ObjCCallbackVideoSink(VideoSinkCallback callback) : callback_(callback) {}
34
Mirko Bonadei17aff352018-07-26 12:20:40 +020035 void OnFrame(const webrtc::VideoFrame &frame) override {
Anders Carlssonfe9d8172018-04-03 11:40:39 +020036 callback_(NativeToObjCVideoFrame(frame));
37 }
38
39 private:
40 VideoSinkCallback callback_;
41};
42
43} // namespace
44
45@interface ObjCVideoTrackSourceTests : XCTestCase
46@end
47
48@implementation ObjCVideoTrackSourceTests {
49 rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> _video_source;
50}
51
52- (void)setUp {
Niels Möllerac0d1832022-01-17 15:26:54 +010053 _video_source = rtc::make_ref_counted<webrtc::ObjCVideoTrackSource>();
Anders Carlssonfe9d8172018-04-03 11:40:39 +020054}
55
56- (void)tearDown {
57 _video_source = NULL;
58}
59
60- (void)testOnCapturedFrameAdaptsFrame {
61 CVPixelBufferRef pixelBufferRef = NULL;
62 CVPixelBufferCreate(
63 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
64
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020065 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
66 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Anders Carlssonfe9d8172018-04-03 11:40:39 +020067
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020068 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
69 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
70 rotation:RTCVideoRotation_0
71 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +020072
73 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
74 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +020075 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +020076 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
77
78 _video_source->OnOutputFormatRequest(640, 360, 30);
79 _video_source->OnCapturedFrame(frame);
80
81 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
82 XCTAssertEqual(video_renderer->width(), 360);
83 XCTAssertEqual(video_renderer->height(), 640);
84
85 CVBufferRelease(pixelBufferRef);
86}
87
Peter Hanspers488eb982018-06-08 14:49:51 +020088- (void)testOnCapturedFrameAdaptsFrameWithAlignment {
89 // Requesting to adapt 1280x720 to 912x514 gives 639x360 without alignment. The 639 causes issues
90 // with some hardware encoders (e.g. HEVC) so in this test we verify that the alignment is set and
91 // respected.
92
93 CVPixelBufferRef pixelBufferRef = NULL;
94 CVPixelBufferCreate(
95 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
96
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020097 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
98 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Peter Hanspers488eb982018-06-08 14:49:51 +020099
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200100 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
101 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
102 rotation:RTCVideoRotation_0
103 timeStampNs:0];
Peter Hanspers488eb982018-06-08 14:49:51 +0200104
105 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
106 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200107 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Peter Hanspers488eb982018-06-08 14:49:51 +0200108 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
109
110 _video_source->OnOutputFormatRequest(912, 514, 30);
111 _video_source->OnCapturedFrame(frame);
112
113 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
114 XCTAssertEqual(video_renderer->width(), 360);
115 XCTAssertEqual(video_renderer->height(), 640);
116
117 CVBufferRelease(pixelBufferRef);
118}
119
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200120- (void)testOnCapturedFrameAdaptationResultsInCommonResolutions {
121 // Some of the most common resolutions used in the wild are 640x360, 480x270 and 320x180.
122 // Make sure that we properly scale down to exactly these resolutions.
123 CVPixelBufferRef pixelBufferRef = NULL;
124 CVPixelBufferCreate(
125 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
126
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200127 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
128 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200129
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200130 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
131 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
132 rotation:RTCVideoRotation_0
133 timeStampNs:0];
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200134
135 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
136 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200137 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200138 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
139
140 _video_source->OnOutputFormatRequest(640, 360, 30);
141 _video_source->OnCapturedFrame(frame);
142
143 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
144 XCTAssertEqual(video_renderer->width(), 360);
145 XCTAssertEqual(video_renderer->height(), 640);
146
147 _video_source->OnOutputFormatRequest(480, 270, 30);
148 _video_source->OnCapturedFrame(frame);
149
150 XCTAssertEqual(video_renderer->num_rendered_frames(), 2);
151 XCTAssertEqual(video_renderer->width(), 270);
152 XCTAssertEqual(video_renderer->height(), 480);
153
154 _video_source->OnOutputFormatRequest(320, 180, 30);
155 _video_source->OnCapturedFrame(frame);
156
157 XCTAssertEqual(video_renderer->num_rendered_frames(), 3);
158 XCTAssertEqual(video_renderer->width(), 180);
159 XCTAssertEqual(video_renderer->height(), 320);
160
161 CVBufferRelease(pixelBufferRef);
162}
163
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200164- (void)testOnCapturedFrameWithoutAdaptation {
165 CVPixelBufferRef pixelBufferRef = NULL;
166 CVPixelBufferCreate(
167 NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
168
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200169 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
170 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
171 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
172 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
173 rotation:RTCVideoRotation_0
174 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200175
176 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200177 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200178 XCTAssertEqual(frame.width, outputFrame.width);
179 XCTAssertEqual(frame.height, outputFrame.height);
180
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200181 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200182 XCTAssertEqual(buffer.cropX, outputBuffer.cropX);
183 XCTAssertEqual(buffer.cropY, outputBuffer.cropY);
184 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
185
186 [callbackExpectation fulfill];
187 });
188
189 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200190 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200191 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
192
193 _video_source->OnOutputFormatRequest(640, 360, 30);
194 _video_source->OnCapturedFrame(frame);
195
196 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
197 CVBufferRelease(pixelBufferRef);
198}
199
200- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation {
201 CVPixelBufferRef pixelBufferRef = NULL;
202 CVPixelBufferCreate(
203 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
204
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200205 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
206 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
207 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
208 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
209 rotation:RTCVideoRotation_0
210 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200211
212 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200213 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200214 XCTAssertEqual(outputFrame.width, 360);
215 XCTAssertEqual(outputFrame.height, 640);
216
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200217 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200218 XCTAssertEqual(outputBuffer.cropX, 0);
219 XCTAssertEqual(outputBuffer.cropY, 0);
220 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
221
222 [callbackExpectation fulfill];
223 });
224
225 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200226 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200227 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
228
229 _video_source->OnOutputFormatRequest(640, 360, 30);
230 _video_source->OnCapturedFrame(frame);
231
232 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
233 CVBufferRelease(pixelBufferRef);
234}
235
236- (void)testOnCapturedFrameCVPixelBufferNeedsCropping {
237 CVPixelBufferRef pixelBufferRef = NULL;
238 CVPixelBufferCreate(
239 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
240
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200241 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
242 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
243 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
244 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
245 rotation:RTCVideoRotation_0
246 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200247
248 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200249 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200250 XCTAssertEqual(outputFrame.width, 360);
251 XCTAssertEqual(outputFrame.height, 640);
252
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200253 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200254 XCTAssertEqual(outputBuffer.cropX, 10);
255 XCTAssertEqual(outputBuffer.cropY, 0);
256 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
257
258 [callbackExpectation fulfill];
259 });
260
261 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200262 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200263 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
264
265 _video_source->OnOutputFormatRequest(640, 360, 30);
266 _video_source->OnCapturedFrame(frame);
267
268 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
269 CVBufferRelease(pixelBufferRef);
270}
271
272- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation {
273 CVPixelBufferRef pixelBufferRef = NULL;
274 CVPixelBufferCreate(
275 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
276
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200277 // Create a frame that's already adapted down.
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200278 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
279 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
280 adaptedWidth:640
281 adaptedHeight:360
282 cropWidth:720
283 cropHeight:1280
284 cropX:0
285 cropY:0];
286 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
287 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
288 rotation:RTCVideoRotation_0
289 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200290
291 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200292 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200293 XCTAssertEqual(outputFrame.width, 480);
294 XCTAssertEqual(outputFrame.height, 270);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200295
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200296 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200297 XCTAssertEqual(outputBuffer.cropX, 0);
298 XCTAssertEqual(outputBuffer.cropY, 0);
299 XCTAssertEqual(outputBuffer.cropWidth, 640);
300 XCTAssertEqual(outputBuffer.cropHeight, 360);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200301 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
302
303 [callbackExpectation fulfill];
304 });
305
306 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200307 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200308 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
309
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200310 _video_source->OnOutputFormatRequest(480, 270, 30);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200311 _video_source->OnCapturedFrame(frame);
312
313 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
314 CVBufferRelease(pixelBufferRef);
315}
316
317- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping {
318 CVPixelBufferRef pixelBufferRef = NULL;
319 CVPixelBufferCreate(
320 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
321
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200322 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
323 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
324 adaptedWidth:370
325 adaptedHeight:640
326 cropWidth:370
327 cropHeight:640
328 cropX:10
329 cropY:0];
330 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
331 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
332 rotation:RTCVideoRotation_0
333 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200334
335 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200336 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200337 XCTAssertEqual(outputFrame.width, 360);
338 XCTAssertEqual(outputFrame.height, 640);
339
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200340 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200341 XCTAssertEqual(outputBuffer.cropX, 14);
342 XCTAssertEqual(outputBuffer.cropY, 0);
343 XCTAssertEqual(outputBuffer.cropWidth, 360);
344 XCTAssertEqual(outputBuffer.cropHeight, 640);
345 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
346
347 [callbackExpectation fulfill];
348 });
349
350 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200351 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200352 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
353
354 _video_source->OnOutputFormatRequest(640, 360, 30);
355 _video_source->OnCapturedFrame(frame);
356
357 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
358 CVBufferRelease(pixelBufferRef);
359}
360
361- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping {
362 CVPixelBufferRef pixelBufferRef = NULL;
363 CVPixelBufferCreate(
364 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
365
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200366 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
367 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
368 adaptedWidth:300
369 adaptedHeight:640
370 cropWidth:300
371 cropHeight:640
372 cropX:40
373 cropY:0];
374 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
375 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
376 rotation:RTCVideoRotation_0
377 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200378
379 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200380 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200381 XCTAssertEqual(outputFrame.width, 300);
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200382 XCTAssertEqual(outputFrame.height, 534);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200383
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200384 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200385 XCTAssertEqual(outputBuffer.cropX, 40);
386 XCTAssertEqual(outputBuffer.cropY, 52);
387 XCTAssertEqual(outputBuffer.cropWidth, 300);
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200388 XCTAssertEqual(outputBuffer.cropHeight, 534);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200389 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
390
391 [callbackExpectation fulfill];
392 });
393
394 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200395 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200396 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
397
398 _video_source->OnOutputFormatRequest(640, 360, 30);
399 _video_source->OnCapturedFrame(frame);
400
401 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
402 CVBufferRelease(pixelBufferRef);
403}
404
405- (void)testOnCapturedFrameI420BufferNeedsAdaptation {
406 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280);
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200407 RTC_OBJC_TYPE(RTCI420Buffer) *buffer =
408 [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer];
409 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
410 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
411 rotation:RTCVideoRotation_0
412 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200413
414 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200415 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200416 XCTAssertEqual(outputFrame.width, 360);
417 XCTAssertEqual(outputFrame.height, 640);
418
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200419 RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200420
421 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
422 XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
423
424 [callbackExpectation fulfill];
425 });
426
427 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200428 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200429 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
430
431 _video_source->OnOutputFormatRequest(640, 360, 30);
432 _video_source->OnCapturedFrame(frame);
433
434 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
435}
436
437- (void)testOnCapturedFrameI420BufferNeedsCropping {
438 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640);
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200439 RTC_OBJC_TYPE(RTCI420Buffer) *buffer =
440 [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer];
441 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
442 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
443 rotation:RTCVideoRotation_0
444 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200445
446 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200447 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200448 XCTAssertEqual(outputFrame.width, 360);
449 XCTAssertEqual(outputFrame.height, 640);
450
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200451 RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200452
453 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
454 XCTAssertGreaterThanOrEqual(psnr, 40);
455
456 [callbackExpectation fulfill];
457 });
458
459 const rtc::VideoSinkWants video_sink_wants;
Niels Möller03486fc2022-04-25 10:46:59 +0200460 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get();
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200461 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
462
463 _video_source->OnOutputFormatRequest(640, 360, 30);
464 _video_source->OnCapturedFrame(frame);
465
466 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
467}
468
469@end