1*d9f75844SAndroid Build Coastguard Worker/* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2018 The WebRTC project authors. All Rights Reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker#import <Foundation/Foundation.h> 12*d9f75844SAndroid Build Coastguard Worker#import <XCTest/XCTest.h> 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker#include "sdk/objc/native/src/objc_video_track_source.h" 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard Worker#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h" 17*d9f75844SAndroid Build Coastguard Worker#import "base/RTCVideoFrame.h" 18*d9f75844SAndroid Build Coastguard Worker#import "base/RTCVideoFrameBuffer.h" 19*d9f75844SAndroid Build Coastguard Worker#import "components/video_frame_buffer/RTCCVPixelBuffer.h" 20*d9f75844SAndroid Build Coastguard Worker#import "frame_buffer_helpers.h" 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Worker#include "api/scoped_refptr.h" 23*d9f75844SAndroid Build Coastguard Worker#include "common_video/libyuv/include/webrtc_libyuv.h" 24*d9f75844SAndroid Build Coastguard Worker#include "media/base/fake_video_renderer.h" 25*d9f75844SAndroid Build Coastguard Worker#include "sdk/objc/native/api/video_frame.h" 26*d9f75844SAndroid Build Coastguard Worker 27*d9f75844SAndroid Build Coastguard Workertypedef void (^VideoSinkCallback)(RTC_OBJC_TYPE(RTCVideoFrame) *); 28*d9f75844SAndroid Build Coastguard Worker 29*d9f75844SAndroid Build Coastguard Workernamespace { 30*d9f75844SAndroid Build Coastguard Worker 31*d9f75844SAndroid Build Coastguard Workerclass ObjCCallbackVideoSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> { 32*d9f75844SAndroid Build Coastguard Worker public: 33*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink(VideoSinkCallback callback) : callback_(callback) {} 34*d9f75844SAndroid Build Coastguard Worker 35*d9f75844SAndroid Build Coastguard Worker void OnFrame(const webrtc::VideoFrame &frame) override { 36*d9f75844SAndroid Build Coastguard Worker callback_(NativeToObjCVideoFrame(frame)); 37*d9f75844SAndroid Build Coastguard Worker } 38*d9f75844SAndroid Build Coastguard Worker 39*d9f75844SAndroid Build Coastguard Worker private: 40*d9f75844SAndroid Build Coastguard Worker VideoSinkCallback callback_; 41*d9f75844SAndroid Build Coastguard Worker}; 42*d9f75844SAndroid Build Coastguard Worker 43*d9f75844SAndroid Build Coastguard Worker} // namespace 44*d9f75844SAndroid Build Coastguard Worker 45*d9f75844SAndroid Build Coastguard Worker@interface ObjCVideoTrackSourceTests : XCTestCase 46*d9f75844SAndroid Build Coastguard Worker@end 47*d9f75844SAndroid Build Coastguard Worker 48*d9f75844SAndroid Build Coastguard Worker@implementation ObjCVideoTrackSourceTests { 49*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<webrtc::ObjCVideoTrackSource> _video_source; 50*d9f75844SAndroid Build Coastguard Worker} 51*d9f75844SAndroid Build Coastguard Worker 52*d9f75844SAndroid Build Coastguard Worker- (void)setUp { 53*d9f75844SAndroid Build Coastguard Worker _video_source = rtc::make_ref_counted<webrtc::ObjCVideoTrackSource>(); 54*d9f75844SAndroid Build Coastguard Worker} 55*d9f75844SAndroid Build Coastguard Worker 56*d9f75844SAndroid Build Coastguard Worker- (void)tearDown { 57*d9f75844SAndroid Build Coastguard Worker _video_source = NULL; 58*d9f75844SAndroid Build Coastguard Worker} 59*d9f75844SAndroid Build Coastguard Worker 60*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameAdaptsFrame { 61*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 62*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 63*d9f75844SAndroid Build Coastguard Worker NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 64*d9f75844SAndroid Build Coastguard Worker 65*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 66*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 67*d9f75844SAndroid Build Coastguard Worker 68*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 69*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 70*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 71*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 72*d9f75844SAndroid Build Coastguard Worker 73*d9f75844SAndroid Build Coastguard Worker cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); 74*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 75*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 76*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); 77*d9f75844SAndroid Build Coastguard Worker 78*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 79*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 80*d9f75844SAndroid Build Coastguard Worker 81*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->num_rendered_frames(), 1); 82*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->width(), 360); 83*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->height(), 640); 84*d9f75844SAndroid Build Coastguard Worker 85*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 86*d9f75844SAndroid Build Coastguard Worker} 87*d9f75844SAndroid Build Coastguard Worker 88*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameAdaptsFrameWithAlignment { 89*d9f75844SAndroid Build Coastguard Worker // Requesting to adapt 1280x720 to 912x514 gives 639x360 without alignment. The 639 causes issues 90*d9f75844SAndroid Build Coastguard Worker // with some hardware encoders (e.g. HEVC) so in this test we verify that the alignment is set and 91*d9f75844SAndroid Build Coastguard Worker // respected. 92*d9f75844SAndroid Build Coastguard Worker 93*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 94*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 95*d9f75844SAndroid Build Coastguard Worker NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 96*d9f75844SAndroid Build Coastguard Worker 97*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 98*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 99*d9f75844SAndroid Build Coastguard Worker 100*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 101*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 102*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 103*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 104*d9f75844SAndroid Build Coastguard Worker 105*d9f75844SAndroid Build Coastguard Worker cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); 106*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 107*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 108*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); 109*d9f75844SAndroid Build Coastguard Worker 110*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(912, 514, 30); 111*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 112*d9f75844SAndroid Build Coastguard Worker 113*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->num_rendered_frames(), 1); 114*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->width(), 360); 115*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->height(), 640); 116*d9f75844SAndroid Build Coastguard Worker 117*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 118*d9f75844SAndroid Build Coastguard Worker} 119*d9f75844SAndroid Build Coastguard Worker 120*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameAdaptationResultsInCommonResolutions { 121*d9f75844SAndroid Build Coastguard Worker // Some of the most common resolutions used in the wild are 640x360, 480x270 and 320x180. 122*d9f75844SAndroid Build Coastguard Worker // Make sure that we properly scale down to exactly these resolutions. 123*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 124*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 125*d9f75844SAndroid Build Coastguard Worker NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 126*d9f75844SAndroid Build Coastguard Worker 127*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 128*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 129*d9f75844SAndroid Build Coastguard Worker 130*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 131*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 132*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 133*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 134*d9f75844SAndroid Build Coastguard Worker 135*d9f75844SAndroid Build Coastguard Worker cricket::FakeVideoRenderer *video_renderer = new cricket::FakeVideoRenderer(); 136*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 137*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 138*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(video_renderer, video_sink_wants); 139*d9f75844SAndroid Build Coastguard Worker 140*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 141*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 142*d9f75844SAndroid Build Coastguard Worker 143*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->num_rendered_frames(), 1); 144*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->width(), 360); 145*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->height(), 640); 146*d9f75844SAndroid Build Coastguard Worker 147*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(480, 270, 30); 148*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 149*d9f75844SAndroid Build Coastguard Worker 150*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->num_rendered_frames(), 2); 151*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->width(), 270); 152*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->height(), 480); 153*d9f75844SAndroid Build Coastguard Worker 154*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(320, 180, 30); 155*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 156*d9f75844SAndroid Build Coastguard Worker 157*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->num_rendered_frames(), 3); 158*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->width(), 180); 159*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(video_renderer->height(), 320); 160*d9f75844SAndroid Build Coastguard Worker 161*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 162*d9f75844SAndroid Build Coastguard Worker} 163*d9f75844SAndroid Build Coastguard Worker 164*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameWithoutAdaptation { 165*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 166*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 167*d9f75844SAndroid Build Coastguard Worker NULL, 360, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 168*d9f75844SAndroid Build Coastguard Worker 169*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 170*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 171*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 172*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 173*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 174*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 175*d9f75844SAndroid Build Coastguard Worker 176*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 177*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 178*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(frame.width, outputFrame.width); 179*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(frame.height, outputFrame.height); 180*d9f75844SAndroid Build Coastguard Worker 181*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 182*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.cropX, outputBuffer.cropX); 183*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.cropY, outputBuffer.cropY); 184*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 185*d9f75844SAndroid Build Coastguard Worker 186*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 187*d9f75844SAndroid Build Coastguard Worker }); 188*d9f75844SAndroid Build Coastguard Worker 189*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 190*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 191*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 192*d9f75844SAndroid Build Coastguard Worker 193*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 194*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 195*d9f75844SAndroid Build Coastguard Worker 196*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 197*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 198*d9f75844SAndroid Build Coastguard Worker} 199*d9f75844SAndroid Build Coastguard Worker 200*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameCVPixelBufferNeedsAdaptation { 201*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 202*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 203*d9f75844SAndroid Build Coastguard Worker NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 204*d9f75844SAndroid Build Coastguard Worker 205*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 206*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 207*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 208*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 209*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 210*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 211*d9f75844SAndroid Build Coastguard Worker 212*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 213*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 214*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 360); 215*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 640); 216*d9f75844SAndroid Build Coastguard Worker 217*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 218*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropX, 0); 219*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropY, 0); 220*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 221*d9f75844SAndroid Build Coastguard Worker 222*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 223*d9f75844SAndroid Build Coastguard Worker }); 224*d9f75844SAndroid Build Coastguard Worker 225*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 226*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 227*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 228*d9f75844SAndroid Build Coastguard Worker 229*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 230*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 231*d9f75844SAndroid Build Coastguard Worker 232*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 233*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 234*d9f75844SAndroid Build Coastguard Worker} 235*d9f75844SAndroid Build Coastguard Worker 236*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameCVPixelBufferNeedsCropping { 237*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 238*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 239*d9f75844SAndroid Build Coastguard Worker NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 240*d9f75844SAndroid Build Coastguard Worker 241*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 242*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef]; 243*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 244*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 245*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 246*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 247*d9f75844SAndroid Build Coastguard Worker 248*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 249*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 250*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 360); 251*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 640); 252*d9f75844SAndroid Build Coastguard Worker 253*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 254*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropX, 10); 255*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropY, 0); 256*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 257*d9f75844SAndroid Build Coastguard Worker 258*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 259*d9f75844SAndroid Build Coastguard Worker }); 260*d9f75844SAndroid Build Coastguard Worker 261*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 262*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 263*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 264*d9f75844SAndroid Build Coastguard Worker 265*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 266*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 267*d9f75844SAndroid Build Coastguard Worker 268*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 269*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 270*d9f75844SAndroid Build Coastguard Worker} 271*d9f75844SAndroid Build Coastguard Worker 272*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFramePreAdaptedCVPixelBufferNeedsAdaptation { 273*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 274*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 275*d9f75844SAndroid Build Coastguard Worker NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 276*d9f75844SAndroid Build Coastguard Worker 277*d9f75844SAndroid Build Coastguard Worker // Create a frame that's already adapted down. 278*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 279*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef 280*d9f75844SAndroid Build Coastguard Worker adaptedWidth:640 281*d9f75844SAndroid Build Coastguard Worker adaptedHeight:360 282*d9f75844SAndroid Build Coastguard Worker cropWidth:720 283*d9f75844SAndroid Build Coastguard Worker cropHeight:1280 284*d9f75844SAndroid Build Coastguard Worker cropX:0 285*d9f75844SAndroid Build Coastguard Worker cropY:0]; 286*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 287*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 288*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 289*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 290*d9f75844SAndroid Build Coastguard Worker 291*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 292*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 293*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 480); 294*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 270); 295*d9f75844SAndroid Build Coastguard Worker 296*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 297*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropX, 0); 298*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropY, 0); 299*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropWidth, 640); 300*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropHeight, 360); 301*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 302*d9f75844SAndroid Build Coastguard Worker 303*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 304*d9f75844SAndroid Build Coastguard Worker }); 305*d9f75844SAndroid Build Coastguard Worker 306*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 307*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 308*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 309*d9f75844SAndroid Build Coastguard Worker 310*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(480, 270, 30); 311*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 312*d9f75844SAndroid Build Coastguard Worker 313*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 314*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 315*d9f75844SAndroid Build Coastguard Worker} 316*d9f75844SAndroid Build Coastguard Worker 317*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFramePreCroppedCVPixelBufferNeedsCropping { 318*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 319*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 320*d9f75844SAndroid Build Coastguard Worker NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 321*d9f75844SAndroid Build Coastguard Worker 322*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 323*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef 324*d9f75844SAndroid Build Coastguard Worker adaptedWidth:370 325*d9f75844SAndroid Build Coastguard Worker adaptedHeight:640 326*d9f75844SAndroid Build Coastguard Worker cropWidth:370 327*d9f75844SAndroid Build Coastguard Worker cropHeight:640 328*d9f75844SAndroid Build Coastguard Worker cropX:10 329*d9f75844SAndroid Build Coastguard Worker cropY:0]; 330*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 331*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 332*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 333*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 334*d9f75844SAndroid Build Coastguard Worker 335*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 336*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 337*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 360); 338*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 640); 339*d9f75844SAndroid Build Coastguard Worker 340*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 341*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropX, 14); 342*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropY, 0); 343*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropWidth, 360); 344*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropHeight, 640); 345*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 346*d9f75844SAndroid Build Coastguard Worker 347*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 348*d9f75844SAndroid Build Coastguard Worker }); 349*d9f75844SAndroid Build Coastguard Worker 350*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 351*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 352*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 353*d9f75844SAndroid Build Coastguard Worker 354*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 355*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 356*d9f75844SAndroid Build Coastguard Worker 357*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 358*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 359*d9f75844SAndroid Build Coastguard Worker} 360*d9f75844SAndroid Build Coastguard Worker 361*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameSmallerPreCroppedCVPixelBufferNeedsCropping { 362*d9f75844SAndroid Build Coastguard Worker CVPixelBufferRef pixelBufferRef = NULL; 363*d9f75844SAndroid Build Coastguard Worker CVPixelBufferCreate( 364*d9f75844SAndroid Build Coastguard Worker NULL, 380, 640, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef); 365*d9f75844SAndroid Build Coastguard Worker 366*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = 367*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef 368*d9f75844SAndroid Build Coastguard Worker adaptedWidth:300 369*d9f75844SAndroid Build Coastguard Worker adaptedHeight:640 370*d9f75844SAndroid Build Coastguard Worker cropWidth:300 371*d9f75844SAndroid Build Coastguard Worker cropHeight:640 372*d9f75844SAndroid Build Coastguard Worker cropX:40 373*d9f75844SAndroid Build Coastguard Worker cropY:0]; 374*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 375*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 376*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 377*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 378*d9f75844SAndroid Build Coastguard Worker 379*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 380*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 381*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 300); 382*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 534); 383*d9f75844SAndroid Build Coastguard Worker 384*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCCVPixelBuffer) *outputBuffer = outputFrame.buffer; 385*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropX, 40); 386*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropY, 52); 387*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropWidth, 300); 388*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputBuffer.cropHeight, 534); 389*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(buffer.pixelBuffer, outputBuffer.pixelBuffer); 390*d9f75844SAndroid Build Coastguard Worker 391*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 392*d9f75844SAndroid Build Coastguard Worker }); 393*d9f75844SAndroid Build Coastguard Worker 394*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 395*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 396*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 397*d9f75844SAndroid Build Coastguard Worker 398*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 399*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 400*d9f75844SAndroid Build Coastguard Worker 401*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 402*d9f75844SAndroid Build Coastguard Worker CVBufferRelease(pixelBufferRef); 403*d9f75844SAndroid Build Coastguard Worker} 404*d9f75844SAndroid Build Coastguard Worker 405*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameI420BufferNeedsAdaptation { 406*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280); 407*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCI420Buffer) *buffer = 408*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer]; 409*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 410*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 411*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 412*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 413*d9f75844SAndroid Build Coastguard Worker 414*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 415*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 416*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 360); 417*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 640); 418*d9f75844SAndroid Build Coastguard Worker 419*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer; 420*d9f75844SAndroid Build Coastguard Worker 421*d9f75844SAndroid Build Coastguard Worker double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]); 422*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(psnr, webrtc::kPerfectPSNR); 423*d9f75844SAndroid Build Coastguard Worker 424*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 425*d9f75844SAndroid Build Coastguard Worker }); 426*d9f75844SAndroid Build Coastguard Worker 427*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 428*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 429*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 430*d9f75844SAndroid Build Coastguard Worker 431*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 432*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 433*d9f75844SAndroid Build Coastguard Worker 434*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 435*d9f75844SAndroid Build Coastguard Worker} 436*d9f75844SAndroid Build Coastguard Worker 437*d9f75844SAndroid Build Coastguard Worker- (void)testOnCapturedFrameI420BufferNeedsCropping { 438*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(380, 640); 439*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCI420Buffer) *buffer = 440*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:i420Buffer]; 441*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCVideoFrame) *frame = 442*d9f75844SAndroid Build Coastguard Worker [[RTC_OBJC_TYPE(RTCVideoFrame) alloc] initWithBuffer:buffer 443*d9f75844SAndroid Build Coastguard Worker rotation:RTCVideoRotation_0 444*d9f75844SAndroid Build Coastguard Worker timeStampNs:0]; 445*d9f75844SAndroid Build Coastguard Worker 446*d9f75844SAndroid Build Coastguard Worker XCTestExpectation *callbackExpectation = [self expectationWithDescription:@"videoSinkCallback"]; 447*d9f75844SAndroid Build Coastguard Worker ObjCCallbackVideoSink callback_video_sink(^void(RTC_OBJC_TYPE(RTCVideoFrame) * outputFrame) { 448*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.width, 360); 449*d9f75844SAndroid Build Coastguard Worker XCTAssertEqual(outputFrame.height, 640); 450*d9f75844SAndroid Build Coastguard Worker 451*d9f75844SAndroid Build Coastguard Worker RTC_OBJC_TYPE(RTCI420Buffer) *outputBuffer = (RTC_OBJC_TYPE(RTCI420Buffer) *)outputFrame.buffer; 452*d9f75844SAndroid Build Coastguard Worker 453*d9f75844SAndroid Build Coastguard Worker double psnr = I420PSNR(*[buffer nativeI420Buffer], *[outputBuffer nativeI420Buffer]); 454*d9f75844SAndroid Build Coastguard Worker XCTAssertGreaterThanOrEqual(psnr, 40); 455*d9f75844SAndroid Build Coastguard Worker 456*d9f75844SAndroid Build Coastguard Worker [callbackExpectation fulfill]; 457*d9f75844SAndroid Build Coastguard Worker }); 458*d9f75844SAndroid Build Coastguard Worker 459*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants video_sink_wants; 460*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<webrtc::VideoFrame> *video_source_interface = _video_source.get(); 461*d9f75844SAndroid Build Coastguard Worker video_source_interface->AddOrUpdateSink(&callback_video_sink, video_sink_wants); 462*d9f75844SAndroid Build Coastguard Worker 463*d9f75844SAndroid Build Coastguard Worker _video_source->OnOutputFormatRequest(640, 360, 30); 464*d9f75844SAndroid Build Coastguard Worker _video_source->OnCapturedFrame(frame); 465*d9f75844SAndroid Build Coastguard Worker 466*d9f75844SAndroid Build Coastguard Worker [self waitForExpectations:@[ callbackExpectation ] timeout:10.0]; 467*d9f75844SAndroid Build Coastguard Worker} 468*d9f75844SAndroid Build Coastguard Worker 469*d9f75844SAndroid Build Coastguard Worker@end 470