blob: ca3d67293f44b94ddfe9bf16952cfcf273be0b28 [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"
25#include "rtc_base/ref_counted_object.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020026#include "sdk/objc/native/api/video_frame.h"
Anders Carlssonfe9d8172018-04-03 11:40:39 +020027
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020028typedef void (^VideoSinkCallback)(RTC_OBJC_TYPE(RTCVideoFrame) *);
Anders Carlssonfe9d8172018-04-03 11:40:39 +020029
30namespace {
31
32class ObjCCallbackVideoSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
33 public:
34 ObjCCallbackVideoSink(VideoSinkCallback callback) : callback_(callback) {}
35
Mirko Bonadei17aff352018-07-26 12:20:40 +020036 void OnFrame(const webrtc::VideoFrame &frame) override {
Anders Carlssonfe9d8172018-04-03 11:40:39 +020037 callback_(NativeToObjCVideoFrame(frame));
38 }
39
40 private:
41 VideoSinkCallback callback_;
42};
43
44} // namespace
45
46@interface ObjCVideoTrackSourceTests : XCTestCase
47@end
48
49@implementation ObjCVideoTrackSourceTests {
50 rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> _video_source;
51}
52
53- (void)setUp {
54 _video_source = new rtc::RefCountedObject<webrtc::ObjCVideoTrackSource>();
55}
56
57- (void)tearDown {
58 _video_source = NULL;
59}
60
61- (void)testOnCapturedFrameAdaptsFrame {
62 CVPixelBufferRef pixelBufferRef = NULL;
63 CVPixelBufferCreate(
64 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
65
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020066 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
67 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Anders Carlssonfe9d8172018-04-03 11:40:39 +020068
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020069 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
70 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
71 rotation:RTCVideoRotation_0
72 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +020073
74 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
75 const rtc::VideoSinkWants video_sink_wants;
76 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
77 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
78
79 _video_source->OnOutputFormatRequest(640, 360, 30);
80 _video_source->OnCapturedFrame(frame);
81
82 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
83 XCTAssertEqual(video_renderer->width(), 360);
84 XCTAssertEqual(video_renderer->height(), 640);
85
86 CVBufferRelease(pixelBufferRef);
87}
88
Peter Hanspers488eb982018-06-08 14:49:51 +020089- (void)testOnCapturedFrameAdaptsFrameWithAlignment {
90 // Requesting to adapt 1280x720 to 912x514 gives 639x360 without alignment. The 639 causes issues
91 // with some hardware encoders (e.g. HEVC) so in this test we verify that the alignment is set and
92 // respected.
93
94 CVPixelBufferRef pixelBufferRef = NULL;
95 CVPixelBufferCreate(
96 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
97
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020098 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
99 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Peter Hanspers488eb982018-06-08 14:49:51 +0200100
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200101 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
102 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
103 rotation:RTCVideoRotation_0
104 timeStampNs:0];
Peter Hanspers488eb982018-06-08 14:49:51 +0200105
106 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
107 const rtc::VideoSinkWants video_sink_wants;
108 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
109 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
110
111 _video_source->OnOutputFormatRequest(912, 514, 30);
112 _video_source->OnCapturedFrame(frame);
113
114 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
115 XCTAssertEqual(video_renderer->width(), 360);
116 XCTAssertEqual(video_renderer->height(), 640);
117
118 CVBufferRelease(pixelBufferRef);
119}
120
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200121- (void)testOnCapturedFrameAdaptationResultsInCommonResolutions {
122 // Some of the most common resolutions used in the wild are 640x360, 480x270 and 320x180.
123 // Make sure that we properly scale down to exactly these resolutions.
124 CVPixelBufferRef pixelBufferRef = NULL;
125 CVPixelBufferCreate(
126 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
127
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200128 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
129 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200130
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200131 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
132 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
133 rotation:RTCVideoRotation_0
134 timeStampNs:0];
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200135
136 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
137 const rtc::VideoSinkWants video_sink_wants;
138 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
139 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
140
141 _video_source->OnOutputFormatRequest(640, 360, 30);
142 _video_source->OnCapturedFrame(frame);
143
144 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
145 XCTAssertEqual(video_renderer->width(), 360);
146 XCTAssertEqual(video_renderer->height(), 640);
147
148 _video_source->OnOutputFormatRequest(480, 270, 30);
149 _video_source->OnCapturedFrame(frame);
150
151 XCTAssertEqual(video_renderer->num_rendered_frames(), 2);
152 XCTAssertEqual(video_renderer->width(), 270);
153 XCTAssertEqual(video_renderer->height(), 480);
154
155 _video_source->OnOutputFormatRequest(320, 180, 30);
156 _video_source->OnCapturedFrame(frame);
157
158 XCTAssertEqual(video_renderer->num_rendered_frames(), 3);
159 XCTAssertEqual(video_renderer->width(), 180);
160 XCTAssertEqual(video_renderer->height(), 320);
161
162 CVBufferRelease(pixelBufferRef);
163}
164
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200165- (void)testOnCapturedFrameWithoutAdaptation {
166 CVPixelBufferRef pixelBufferRef = NULL;
167 CVPixelBufferCreate(
168 NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
169
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200170 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
171 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
172 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
173 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
174 rotation:RTCVideoRotation_0
175 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200176
177 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200178 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200179 XCTAssertEqual(frame.width, outputFrame.width);
180 XCTAssertEqual(frame.height, outputFrame.height);
181
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200182 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200183 XCTAssertEqual(buffer.cropX, outputBuffer.cropX);
184 XCTAssertEqual(buffer.cropY, outputBuffer.cropY);
185 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
186
187 [callbackExpectation fulfill];
188 });
189
190 const rtc::VideoSinkWants video_sink_wants;
191 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
192 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
193
194 _video_source->OnOutputFormatRequest(640, 360, 30);
195 _video_source->OnCapturedFrame(frame);
196
197 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
198 CVBufferRelease(pixelBufferRef);
199}
200
201- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation {
202 CVPixelBufferRef pixelBufferRef = NULL;
203 CVPixelBufferCreate(
204 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
205
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200206 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
207 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
208 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
209 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
210 rotation:RTCVideoRotation_0
211 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200212
213 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200214 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200215 XCTAssertEqual(outputFrame.width, 360);
216 XCTAssertEqual(outputFrame.height, 640);
217
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200218 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200219 XCTAssertEqual(outputBuffer.cropX, 0);
220 XCTAssertEqual(outputBuffer.cropY, 0);
221 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
222
223 [callbackExpectation fulfill];
224 });
225
226 const rtc::VideoSinkWants video_sink_wants;
227 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
228 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
229
230 _video_source->OnOutputFormatRequest(640, 360, 30);
231 _video_source->OnCapturedFrame(frame);
232
233 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
234 CVBufferRelease(pixelBufferRef);
235}
236
237- (void)testOnCapturedFrameCVPixelBufferNeedsCropping {
238 CVPixelBufferRef pixelBufferRef = NULL;
239 CVPixelBufferCreate(
240 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
241
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200242 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
243 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
244 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
245 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
246 rotation:RTCVideoRotation_0
247 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200248
249 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200250 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200251 XCTAssertEqual(outputFrame.width, 360);
252 XCTAssertEqual(outputFrame.height, 640);
253
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200254 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200255 XCTAssertEqual(outputBuffer.cropX, 10);
256 XCTAssertEqual(outputBuffer.cropY, 0);
257 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
258
259 [callbackExpectation fulfill];
260 });
261
262 const rtc::VideoSinkWants video_sink_wants;
263 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
264 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
265
266 _video_source->OnOutputFormatRequest(640, 360, 30);
267 _video_source->OnCapturedFrame(frame);
268
269 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
270 CVBufferRelease(pixelBufferRef);
271}
272
273- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation {
274 CVPixelBufferRef pixelBufferRef = NULL;
275 CVPixelBufferCreate(
276 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
277
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200278 // Create a frame that's already adapted down.
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200279 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
280 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
281 adaptedWidth:640
282 adaptedHeight:360
283 cropWidth:720
284 cropHeight:1280
285 cropX:0
286 cropY:0];
287 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
288 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
289 rotation:RTCVideoRotation_0
290 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200291
292 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200293 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200294 XCTAssertEqual(outputFrame.width, 480);
295 XCTAssertEqual(outputFrame.height, 270);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200296
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200297 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200298 XCTAssertEqual(outputBuffer.cropX, 0);
299 XCTAssertEqual(outputBuffer.cropY, 0);
300 XCTAssertEqual(outputBuffer.cropWidth, 640);
301 XCTAssertEqual(outputBuffer.cropHeight, 360);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200302 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
303
304 [callbackExpectation fulfill];
305 });
306
307 const rtc::VideoSinkWants video_sink_wants;
308 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
309 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
310
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200311 _video_source->OnOutputFormatRequest(480, 270, 30);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200312 _video_source->OnCapturedFrame(frame);
313
314 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
315 CVBufferRelease(pixelBufferRef);
316}
317
318- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping {
319 CVPixelBufferRef pixelBufferRef = NULL;
320 CVPixelBufferCreate(
321 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
322
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200323 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
324 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
325 adaptedWidth:370
326 adaptedHeight:640
327 cropWidth:370
328 cropHeight:640
329 cropX:10
330 cropY:0];
331 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
332 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
333 rotation:RTCVideoRotation_0
334 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200335
336 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200337 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200338 XCTAssertEqual(outputFrame.width, 360);
339 XCTAssertEqual(outputFrame.height, 640);
340
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200341 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200342 XCTAssertEqual(outputBuffer.cropX, 14);
343 XCTAssertEqual(outputBuffer.cropY, 0);
344 XCTAssertEqual(outputBuffer.cropWidth, 360);
345 XCTAssertEqual(outputBuffer.cropHeight, 640);
346 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
347
348 [callbackExpectation fulfill];
349 });
350
351 const rtc::VideoSinkWants video_sink_wants;
352 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
353 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
354
355 _video_source->OnOutputFormatRequest(640, 360, 30);
356 _video_source->OnCapturedFrame(frame);
357
358 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
359 CVBufferRelease(pixelBufferRef);
360}
361
362- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping {
363 CVPixelBufferRef pixelBufferRef = NULL;
364 CVPixelBufferCreate(
365 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
366
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200367 RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
368 [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
369 adaptedWidth:300
370 adaptedHeight:640
371 cropWidth:300
372 cropHeight:640
373 cropX:40
374 cropY:0];
375 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
376 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
377 rotation:RTCVideoRotation_0
378 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200379
380 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200381 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200382 XCTAssertEqual(outputFrame.width, 300);
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200383 XCTAssertEqual(outputFrame.height, 534);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200384
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200385 RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200386 XCTAssertEqual(outputBuffer.cropX, 40);
387 XCTAssertEqual(outputBuffer.cropY, 52);
388 XCTAssertEqual(outputBuffer.cropWidth, 300);
Kári Tristan Helgasonbf45add2019-08-23 12:57:50 +0200389 XCTAssertEqual(outputBuffer.cropHeight, 534);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200390 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
391
392 [callbackExpectation fulfill];
393 });
394
395 const rtc::VideoSinkWants video_sink_wants;
396 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
397 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
398
399 _video_source->OnOutputFormatRequest(640, 360, 30);
400 _video_source->OnCapturedFrame(frame);
401
402 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
403 CVBufferRelease(pixelBufferRef);
404}
405
406- (void)testOnCapturedFrameI420BufferNeedsAdaptation {
407 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280);
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200408 RTC_OBJC_TYPE(RTCI420Buffer) *buffer =
409 [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer];
410 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
411 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
412 rotation:RTCVideoRotation_0
413 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200414
415 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200416 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200417 XCTAssertEqual(outputFrame.width, 360);
418 XCTAssertEqual(outputFrame.height, 640);
419
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200420 RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200421
422 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
423 XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
424
425 [callbackExpectation fulfill];
426 });
427
428 const rtc::VideoSinkWants video_sink_wants;
429 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
430 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
431
432 _video_source->OnOutputFormatRequest(640, 360, 30);
433 _video_source->OnCapturedFrame(frame);
434
435 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
436}
437
438- (void)testOnCapturedFrameI420BufferNeedsCropping {
439 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640);
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200440 RTC_OBJC_TYPE(RTCI420Buffer) *buffer =
441 [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer];
442 RTC_OBJC_TYPE(RTCVideoFrame) *frame =
443 [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer
444 rotation:RTCVideoRotation_0
445 timeStampNs:0];
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200446
447 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200448 ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) {
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200449 XCTAssertEqual(outputFrame.width, 360);
450 XCTAssertEqual(outputFrame.height, 640);
451
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200452 RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer;
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200453
454 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
455 XCTAssertGreaterThanOrEqual(psnr, 40);
456
457 [callbackExpectation fulfill];
458 });
459
460 const rtc::VideoSinkWants video_sink_wants;
461 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
462 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
463
464 _video_source->OnOutputFormatRequest(640, 360, 30);
465 _video_source->OnCapturedFrame(frame);
466
467 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
468}
469
470@end