blob: d2ed3985258a2bc5ca5d010d4a94a5a5e5aca0d4 [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
84- (void)testOnCapturedFrameWithoutAdaptation {
85 CVPixelBufferRef pixelBufferRef = NULL;
86 CVPixelBufferCreate(
87 NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
88
89 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
90 RTCVideoFrame *frame =
91 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
92
93 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
94 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
95 XCTAssertEqual(frame.width, outputFrame.width);
96 XCTAssertEqual(frame.height, outputFrame.height);
97
98 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
99 XCTAssertEqual(buffer.cropX, outputBuffer.cropX);
100 XCTAssertEqual(buffer.cropY, outputBuffer.cropY);
101 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
102
103 [callbackExpectation fulfill];
104 });
105
106 const rtc::VideoSinkWants video_sink_wants;
107 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
108 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
109
110 _video_source->OnOutputFormatRequest(640, 360, 30);
111 _video_source->OnCapturedFrame(frame);
112
113 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
114 CVBufferRelease(pixelBufferRef);
115}
116
117- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation {
118 CVPixelBufferRef pixelBufferRef = NULL;
119 CVPixelBufferCreate(
120 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
121
122 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
123 RTCVideoFrame *frame =
124 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
125
126 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
127 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
128 XCTAssertEqual(outputFrame.width, 360);
129 XCTAssertEqual(outputFrame.height, 640);
130
131 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
132 XCTAssertEqual(outputBuffer.cropX, 0);
133 XCTAssertEqual(outputBuffer.cropY, 0);
134 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
135
136 [callbackExpectation fulfill];
137 });
138
139 const rtc::VideoSinkWants video_sink_wants;
140 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
141 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
142
143 _video_source->OnOutputFormatRequest(640, 360, 30);
144 _video_source->OnCapturedFrame(frame);
145
146 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
147 CVBufferRelease(pixelBufferRef);
148}
149
150- (void)testOnCapturedFrameCVPixelBufferNeedsCropping {
151 CVPixelBufferRef pixelBufferRef = NULL;
152 CVPixelBufferCreate(
153 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
154
155 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef];
156 RTCVideoFrame *frame =
157 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
158
159 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
160 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
161 XCTAssertEqual(outputFrame.width, 360);
162 XCTAssertEqual(outputFrame.height, 640);
163
164 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
165 XCTAssertEqual(outputBuffer.cropX, 10);
166 XCTAssertEqual(outputBuffer.cropY, 0);
167 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
168
169 [callbackExpectation fulfill];
170 });
171
172 const rtc::VideoSinkWants video_sink_wants;
173 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
174 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
175
176 _video_source->OnOutputFormatRequest(640, 360, 30);
177 _video_source->OnCapturedFrame(frame);
178
179 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
180 CVBufferRelease(pixelBufferRef);
181}
182
183- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation {
184 CVPixelBufferRef pixelBufferRef = NULL;
185 CVPixelBufferCreate(
186 NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
187
188 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
189 adaptedWidth:700
190 adaptedHeight:700
191 cropWidth:720
192 cropHeight:1280
193 cropX:0
194 cropY:0];
195 RTCVideoFrame *frame =
196 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
197
198 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
199 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
200 XCTAssertEqual(outputFrame.width, 297);
201 XCTAssertEqual(outputFrame.height, 525);
202
203 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
204 XCTAssertEqual(outputBuffer.cropX, 152);
205 XCTAssertEqual(outputBuffer.cropY, 0);
206 XCTAssertEqual(outputBuffer.cropWidth, 396);
207 XCTAssertEqual(outputBuffer.cropHeight, 700);
208 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
209
210 [callbackExpectation fulfill];
211 });
212
213 const rtc::VideoSinkWants video_sink_wants;
214 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
215 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
216
217 _video_source->OnOutputFormatRequest(640, 360, 30);
218 _video_source->OnCapturedFrame(frame);
219
220 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
221 CVBufferRelease(pixelBufferRef);
222}
223
224- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping {
225 CVPixelBufferRef pixelBufferRef = NULL;
226 CVPixelBufferCreate(
227 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
228
229 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
230 adaptedWidth:370
231 adaptedHeight:640
232 cropWidth:370
233 cropHeight:640
234 cropX:10
235 cropY:0];
236 RTCVideoFrame *frame =
237 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
238
239 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
240 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
241 XCTAssertEqual(outputFrame.width, 360);
242 XCTAssertEqual(outputFrame.height, 640);
243
244 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
245 XCTAssertEqual(outputBuffer.cropX, 14);
246 XCTAssertEqual(outputBuffer.cropY, 0);
247 XCTAssertEqual(outputBuffer.cropWidth, 360);
248 XCTAssertEqual(outputBuffer.cropHeight, 640);
249 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
250
251 [callbackExpectation fulfill];
252 });
253
254 const rtc::VideoSinkWants video_sink_wants;
255 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
256 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
257
258 _video_source->OnOutputFormatRequest(640, 360, 30);
259 _video_source->OnCapturedFrame(frame);
260
261 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
262 CVBufferRelease(pixelBufferRef);
263}
264
265- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping {
266 CVPixelBufferRef pixelBufferRef = NULL;
267 CVPixelBufferCreate(
268 NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
269
270 RTCCVPixelBuffer *buffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBufferRef
271 adaptedWidth:300
272 adaptedHeight:640
273 cropWidth:300
274 cropHeight:640
275 cropX:40
276 cropY:0];
277 RTCVideoFrame *frame =
278 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
279
280 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
281 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
282 XCTAssertEqual(outputFrame.width, 300);
283 XCTAssertEqual(outputFrame.height, 533);
284
285 RTCCVPixelBuffer *outputBuffer = outputFrame.buffer;
286 XCTAssertEqual(outputBuffer.cropX, 40);
287 XCTAssertEqual(outputBuffer.cropY, 52);
288 XCTAssertEqual(outputBuffer.cropWidth, 300);
289 XCTAssertEqual(outputBuffer.cropHeight, 533);
290 XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer);
291
292 [callbackExpectation fulfill];
293 });
294
295 const rtc::VideoSinkWants video_sink_wants;
296 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
297 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
298
299 _video_source->OnOutputFormatRequest(640, 360, 30);
300 _video_source->OnCapturedFrame(frame);
301
302 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
303 CVBufferRelease(pixelBufferRef);
304}
305
306- (void)testOnCapturedFrameI420BufferNeedsAdaptation {
307 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280);
308 RTCI420Buffer *buffer = [[RTCI420Buffer alloc] initWithFrameBuffer:i420Buffer];
309 RTCVideoFrame *frame =
310 [[RTCVideoFrame alloc] initWithBuffer:buffer rotation:RTCVideoRotation_0 timeStampNs:0];
311
312 XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"];
313 ObjCCallbackVideoSink callback_video_sink(^void(RTCVideoFrame *outputFrame) {
314 XCTAssertEqual(outputFrame.width, 360);
315 XCTAssertEqual(outputFrame.height, 640);
316
317 RTCI420Buffer *outputBuffer = (RTCI420Buffer *)outputFrame.buffer;
318
319 double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]);
320 XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
321
322 [callbackExpectation fulfill];
323 });
324
325 const rtc::VideoSinkWants video_sink_wants;
326 rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source;
327 video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants);
328
329 _video_source->OnOutputFormatRequest(640, 360, 30);
330 _video_source->OnCapturedFrame(frame);
331
332 [self waitForExpectations:@[ callbackExpectation ] timeout:10.0];
333}
334
335- (void)testOnCapturedFrameI420BufferNeedsCropping {
336 rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640);
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 XCTAssertGreaterThanOrEqual(psnr, 40);
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@end