blob: 7d111c8b128b73e634e5af294802e68ef5c1f9ed [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
14#include "sdk/objc/Framework/Native/src/objc_video_track_source.h"
15
16#import "Video/RTCI420Buffer+Private.h"
17#import "WebRTC/RTCVideoFrame.h"
18#import "WebRTC/RTCVideoFrameBuffer.h"
19#include "common_video/libyuv/include/webrtc_libyuv.h"
20#include "media/base/fakevideorenderer.h"
21#include "rtc_base/refcountedobject.h"
22#include "rtc_base/scoped_ref_ptr.h"
23#include "sdk/objc/Framework/Native/api/video_frame.h"
24#import "sdk/objc/Framework/UnitTests/frame_buffer_helpers.h"
25
26typedef void (^VideoSinkCallback)(RTCVideoFrame *);
27
28namespace {
29
30class ObjCCallbackVideoSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
31 public:
32 ObjCCallbackVideoSink(VideoSinkCallback callback) : callback_(callback) {}
33
34 virtual void OnFrame(const webrtc::VideoFrame &frame) {
35 callback_(NativeToObjCVideoFrame(frame));
36 }
37
38 private:
39 VideoSinkCallback callback_;
40};
41
42} // namespace
43
44@interface ObjCVideoTrackSourceTests : XCTestCase
45@end
46
47@implementation ObjCVideoTrackSourceTests {
48 rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> _video_source;
49}
50
51- (void)setUp {
52 _video_source = new rtc::RefCountedObject<webrtc::ObjCVideoTrackSource>();
53}
54
55- (void)tearDown {
56 _video_source = NULL;
57}
58
59- (void)testOnCapturedFrameAdaptsFrame {
60 CVPixelBufferRef pixelBufferRef = NULL;
61 CVPixelBufferCreate(
62 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
63
64 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
65
66 RTCVideoFrame *frame =
67 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
68
69 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
70 const rtc::VideoSinkWants video_sink_wants;
71 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
72 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
73
74 _video_source->OnOutputFormatRequest(640, 360, 30);
75 _video_source->OnCapturedFrame(frame);
76
77 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
78 XCTAssertEqual(video_renderer->width(), 360);
79 XCTAssertEqual(video_renderer->height(), 640);
80
81 CVBufferRelease(pixelBufferRef);
82}
83
Peter Hanspers488eb982018-06-08 14:49:51 +020084- (void)testOnCapturedFrameAdaptsFrameWithAlignment {
85 // Requesting to adapt 1280x720 to 912x514 gives 639x360 without alignment. The 639 causes issues
86 // with some hardware encoders (e.g. HEVC) so in this test we verify that the alignment is set and
87 // respected.
88
89 CVPixelBufferRef pixelBufferRef = NULL;
90 CVPixelBufferCreate(
91 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
92
93 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
94
95 RTCVideoFrame *frame =
96 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
97
98 cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer();
99 const rtc::VideoSinkWants video_sink_wants;
100 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
101 video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants);
102
103 _video_source->OnOutputFormatRequest(912, 514, 30);
104 _video_source->OnCapturedFrame(frame);
105
106 XCTAssertEqual(video_renderer->num_rendered_frames(), 1);
107 XCTAssertEqual(video_renderer->width(), 360);
108 XCTAssertEqual(video_renderer->height(), 640);
109
110 CVBufferRelease(pixelBufferRef);
111}
112
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200113- (void)testOnCapturedFrameWithoutAdaptation {
114 CVPixelBufferRef pixelBufferRef = NULL;
115 CVPixelBufferCreate(
116 NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
117
118 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
119 RTCVideoFrame *frame =
120 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
121
122 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
123 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
124 XCTAssertEqual(frame.width, outputFrame.width);
125 XCTAssertEqual(frame.height, outputFrame.height);
126
127 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
128 XCTAssertEqual(buffer.cropX, outputBuffer.cropX);
129 XCTAssertEqual(buffer.cropY, outputBuffer.cropY);
130 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
131
132 [callbackExpectation fulfill];
133 });
134
135 const rtc::VideoSinkWants video_sink_wants;
136 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
137 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
138
139 _video_source->OnOutputFormatRequest(640, 360, 30);
140 _video_source->OnCapturedFrame(frame);
141
142 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
143 CVBufferRelease(pixelBufferRef);
144}
145
146- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation {
147 CVPixelBufferRef pixelBufferRef = NULL;
148 CVPixelBufferCreate(
149 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
150
151 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
152 RTCVideoFrame *frame =
153 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
154
155 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
156 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
157 XCTAssertEqual(outputFrame.width, 360);
158 XCTAssertEqual(outputFrame.height, 640);
159
160 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
161 XCTAssertEqual(outputBuffer.cropX, 0);
162 XCTAssertEqual(outputBuffer.cropY, 0);
163 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
164
165 [callbackExpectation fulfill];
166 });
167
168 const rtc::VideoSinkWants video_sink_wants;
169 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
170 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
171
172 _video_source->OnOutputFormatRequest(640, 360, 30);
173 _video_source->OnCapturedFrame(frame);
174
175 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
176 CVBufferRelease(pixelBufferRef);
177}
178
179- (void)testOnCapturedFrameCVPixelBufferNeedsCropping {
180 CVPixelBufferRef pixelBufferRef = NULL;
181 CVPixelBufferCreate(
182 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
183
184 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
185 RTCVideoFrame *frame =
186 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
187
188 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
189 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
190 XCTAssertEqual(outputFrame.width, 360);
191 XCTAssertEqual(outputFrame.height, 640);
192
193 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
194 XCTAssertEqual(outputBuffer.cropX, 10);
195 XCTAssertEqual(outputBuffer.cropY, 0);
196 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
197
198 [callbackExpectation fulfill];
199 });
200
201 const rtc::VideoSinkWants video_sink_wants;
202 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
203 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
204
205 _video_source->OnOutputFormatRequest(640, 360, 30);
206 _video_source->OnCapturedFrame(frame);
207
208 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
209 CVBufferRelease(pixelBufferRef);
210}
211
212- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation {
213 CVPixelBufferRef pixelBufferRef = NULL;
214 CVPixelBufferCreate(
215 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
216
217 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
218 adaptedWidth:700
219 adaptedHeight:700
220 cropWidth:720
221 cropHeight:1280
222 cropX:0
223 cropY:0];
224 RTCVideoFrame *frame =
225 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
226
227 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
228 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
Peter Hanspers488eb982018-06-08 14:49:51 +0200229 XCTAssertEqual(outputFrame.width, 300);
230 XCTAssertEqual(outputFrame.height, 516);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200231
232 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
Peter Hanspers488eb982018-06-08 14:49:51 +0200233 XCTAssertEqual(outputBuffer.cropX, 150);
234 XCTAssertEqual(outputBuffer.cropY, 6);
235 XCTAssertEqual(outputBuffer.cropWidth, 400);
236 XCTAssertEqual(outputBuffer.cropHeight, 688);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200237 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
238
239 [callbackExpectation fulfill];
240 });
241
242 const rtc::VideoSinkWants video_sink_wants;
243 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
244 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
245
246 _video_source->OnOutputFormatRequest(640, 360, 30);
247 _video_source->OnCapturedFrame(frame);
248
249 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
250 CVBufferRelease(pixelBufferRef);
251}
252
253- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping {
254 CVPixelBufferRef pixelBufferRef = NULL;
255 CVPixelBufferCreate(
256 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
257
258 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
259 adaptedWidth:370
260 adaptedHeight:640
261 cropWidth:370
262 cropHeight:640
263 cropX:10
264 cropY:0];
265 RTCVideoFrame *frame =
266 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
267
268 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
269 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
270 XCTAssertEqual(outputFrame.width, 360);
271 XCTAssertEqual(outputFrame.height, 640);
272
273 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
274 XCTAssertEqual(outputBuffer.cropX, 14);
275 XCTAssertEqual(outputBuffer.cropY, 0);
276 XCTAssertEqual(outputBuffer.cropWidth, 360);
277 XCTAssertEqual(outputBuffer.cropHeight, 640);
278 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
279
280 [callbackExpectation fulfill];
281 });
282
283 const rtc::VideoSinkWants video_sink_wants;
284 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
285 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
286
287 _video_source->OnOutputFormatRequest(640, 360, 30);
288 _video_source->OnCapturedFrame(frame);
289
290 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
291 CVBufferRelease(pixelBufferRef);
292}
293
294- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping {
295 CVPixelBufferRef pixelBufferRef = NULL;
296 CVPixelBufferCreate(
297 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
298
299 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
300 adaptedWidth:300
301 adaptedHeight:640
302 cropWidth:300
303 cropHeight:640
304 cropX:40
305 cropY:0];
306 RTCVideoFrame *frame =
307 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
308
309 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
310 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
311 XCTAssertEqual(outputFrame.width, 300);
Peter Hanspers488eb982018-06-08 14:49:51 +0200312 XCTAssertEqual(outputFrame.height, 536);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200313
314 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
315 XCTAssertEqual(outputBuffer.cropX, 40);
316 XCTAssertEqual(outputBuffer.cropY, 52);
317 XCTAssertEqual(outputBuffer.cropWidth, 300);
Peter Hanspers488eb982018-06-08 14:49:51 +0200318 XCTAssertEqual(outputBuffer.cropHeight, 536);
Anders Carlssonfe9d8172018-04-03 11:40:39 +0200319 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
320
321 [callbackExpectation fulfill];
322 });
323
324 const rtc::VideoSinkWants video_sink_wants;
325 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
326 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
327
328 _video_source->OnOutputFormatRequest(640, 360, 30);
329 _video_source->OnCapturedFrame(frame);
330
331 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
332 CVBufferRelease(pixelBufferRef);
333}
334
335- (void)testOnCapturedFrameI420BufferNeedsAdaptation {
336 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280);
337 RTCI420Buffer *buffer = [[RTCI420Buffer alloc] initWithFrameBuffer:i420Buffer];
338 RTCVideoFrame *frame =
339 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
340
341 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
342 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
343 XCTAssertEqual(outputFrame.width, 360);
344 XCTAssertEqual(outputFrame.height, 640);
345
346 RTCI420Buffer *outputBuffer = (RTCI420Buffer *)outputFrame.buffer;
347
348 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
349 XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
350
351 [callbackExpectation fulfill];
352 });
353
354 const rtc::VideoSinkWants video_sink_wants;
355 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
356 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
357
358 _video_source->OnOutputFormatRequest(640, 360, 30);
359 _video_source->OnCapturedFrame(frame);
360
361 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
362}
363
364- (void)testOnCapturedFrameI420BufferNeedsCropping {
365 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640);
366 RTCI420Buffer *buffer = [[RTCI420Buffer alloc] initWithFrameBuffer:i420Buffer];
367 RTCVideoFrame *frame =
368 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
369
370 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
371 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
372 XCTAssertEqual(outputFrame.width, 360);
373 XCTAssertEqual(outputFrame.height, 640);
374
375 RTCI420Buffer *outputBuffer = (RTCI420Buffer *)outputFrame.buffer;
376
377 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
378 XCTAssertGreaterThanOrEqual(psnr, 40);
379
380 [callbackExpectation fulfill];
381 });
382
383 const rtc::VideoSinkWants video_sink_wants;
384 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
385 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
386
387 _video_source->OnOutputFormatRequest(640, 360, 30);
388 _video_source->OnCapturedFrame(frame);
389
390 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
391}
392
393@end