You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
12 KiB
309 lines
12 KiB
/*
|
|
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "components/video_frame_buffer/RTCCVPixelBuffer.h"
|
|
|
|
#import "api/video_frame_buffer/RTCNativeI420Buffer+Private.h"
|
|
#import "base/RTCVideoFrame.h"
|
|
#import "base/RTCVideoFrameBuffer.h"
|
|
#import "frame_buffer_helpers.h"
|
|
|
|
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
|
#include "third_party/libyuv/include/libyuv.h"
|
|
|
|
@interface RTCCVPixelBufferTests : XCTestCase
|
|
@end
|
|
|
|
@implementation RTCCVPixelBufferTests {
|
|
}
|
|
|
|
- (void)testRequiresCroppingNoCrop {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
|
|
XCTAssertFalse([buffer requiresCropping]);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testRequiresCroppingWithCrop {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *croppedBuffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
|
|
adaptedWidth:720
|
|
adaptedHeight:1280
|
|
cropWidth:360
|
|
cropHeight:640
|
|
cropX:100
|
|
cropY:100];
|
|
|
|
XCTAssertTrue([croppedBuffer requiresCropping]);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testRequiresScalingNoScale {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
XCTAssertFalse([buffer requiresScalingToWidth:720 height:1280]);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testRequiresScalingWithScale {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
XCTAssertTrue([buffer requiresScalingToWidth:360 height:640]);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testRequiresScalingWithScaleAndMatchingCrop {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef
|
|
adaptedWidth:720
|
|
adaptedHeight:1280
|
|
cropWidth:360
|
|
cropHeight:640
|
|
cropX:100
|
|
cropY:100];
|
|
XCTAssertFalse([buffer requiresScalingToWidth:360 height:640]);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testBufferSize_NV12 {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, 720, 1280, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, NULL, &pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
XCTAssertEqual([buffer bufferSizeForCroppingAndScalingToWidth:360 height:640], 576000);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testBufferSize_RGB {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(NULL, 720, 1280, kCVPixelFormatType_32BGRA, NULL, &pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
XCTAssertEqual([buffer bufferSizeForCroppingAndScalingToWidth:360 height:640], 0);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)testCropAndScale_NV12 {
|
|
[self cropAndScaleTestWithNV12];
|
|
}
|
|
|
|
- (void)testCropAndScaleNoOp_NV12 {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputSize:CGSizeMake(720, 1280)];
|
|
}
|
|
|
|
- (void)testCropAndScale_NV12FullToVideo {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange];
|
|
}
|
|
|
|
- (void)testCropAndScaleZeroSizeFrame_NV12 {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputSize:CGSizeMake(0, 0)];
|
|
}
|
|
|
|
- (void)testCropAndScaleToSmallFormat_NV12 {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputSize:CGSizeMake(148, 320)];
|
|
}
|
|
|
|
- (void)testCropAndScaleToOddFormat_NV12 {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputSize:CGSizeMake(361, 640)];
|
|
}
|
|
|
|
- (void)testCropAndScale_32BGRA {
|
|
[self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32BGRA];
|
|
}
|
|
|
|
- (void)testCropAndScale_32ARGB {
|
|
[self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB];
|
|
}
|
|
|
|
- (void)testCropAndScaleWithSmallCropInfo_32ARGB {
|
|
[self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB cropX:2 cropY:3];
|
|
}
|
|
|
|
- (void)testCropAndScaleWithLargeCropInfo_32ARGB {
|
|
[self cropAndScaleTestWithRGBPixelFormat:kCVPixelFormatType_32ARGB cropX:200 cropY:300];
|
|
}
|
|
|
|
- (void)testToI420_NV12 {
|
|
[self toI420WithPixelFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange];
|
|
}
|
|
|
|
- (void)testToI420_32BGRA {
|
|
[self toI420WithPixelFormat:kCVPixelFormatType_32BGRA];
|
|
}
|
|
|
|
- (void)testToI420_32ARGB {
|
|
[self toI420WithPixelFormat:kCVPixelFormatType_32ARGB];
|
|
}
|
|
|
|
#pragma mark - Shared test code
|
|
|
|
- (void)cropAndScaleTestWithNV12 {
|
|
[self cropAndScaleTestWithNV12InputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
outputFormat:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange];
|
|
}
|
|
|
|
- (void)cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat outputFormat:(OSType)outputFormat {
|
|
[self cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat
|
|
outputFormat:(OSType)outputFormat
|
|
outputSize:CGSizeMake(360, 640)];
|
|
}
|
|
|
|
- (void)cropAndScaleTestWithNV12InputFormat:(OSType)inputFormat
|
|
outputFormat:(OSType)outputFormat
|
|
outputSize:(CGSize)outputSize {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(NULL, 720, 1280, inputFormat, NULL, &pixelBufferRef);
|
|
|
|
rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(720, 1280);
|
|
CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
XCTAssertEqual(buffer.width, 720);
|
|
XCTAssertEqual(buffer.height, 1280);
|
|
|
|
CVPixelBufferRef outputPixelBufferRef = NULL;
|
|
CVPixelBufferCreate(
|
|
NULL, outputSize.width, outputSize.height, outputFormat, NULL, &outputPixelBufferRef);
|
|
|
|
std::vector<uint8_t> frameScaleBuffer;
|
|
if ([buffer requiresScalingToWidth:outputSize.width height:outputSize.height]) {
|
|
int size =
|
|
[buffer bufferSizeForCroppingAndScalingToWidth:outputSize.width height:outputSize.height];
|
|
frameScaleBuffer.resize(size);
|
|
} else {
|
|
frameScaleBuffer.clear();
|
|
}
|
|
frameScaleBuffer.shrink_to_fit();
|
|
|
|
[buffer cropAndScaleTo:outputPixelBufferRef withTempBuffer:frameScaleBuffer.data()];
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *scaledBuffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:outputPixelBufferRef];
|
|
XCTAssertEqual(scaledBuffer.width, outputSize.width);
|
|
XCTAssertEqual(scaledBuffer.height, outputSize.height);
|
|
|
|
if (outputSize.width > 0 && outputSize.height > 0) {
|
|
RTC_OBJC_TYPE(RTCI420Buffer) *originalBufferI420 = [buffer toI420];
|
|
RTC_OBJC_TYPE(RTCI420Buffer) *scaledBufferI420 = [scaledBuffer toI420];
|
|
double psnr =
|
|
I420PSNR(*[originalBufferI420 nativeI420Buffer], *[scaledBufferI420 nativeI420Buffer]);
|
|
XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
|
|
}
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)cropAndScaleTestWithRGBPixelFormat:(OSType)pixelFormat {
|
|
[self cropAndScaleTestWithRGBPixelFormat:pixelFormat cropX:0 cropY:0];
|
|
}
|
|
|
|
- (void)cropAndScaleTestWithRGBPixelFormat:(OSType)pixelFormat cropX:(int)cropX cropY:(int)cropY {
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(NULL, 720, 1280, pixelFormat, NULL, &pixelBufferRef);
|
|
|
|
DrawGradientInRGBPixelBuffer(pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc]
|
|
initWithPixelBuffer:pixelBufferRef
|
|
adaptedWidth:CVPixelBufferGetWidth(pixelBufferRef)
|
|
adaptedHeight:CVPixelBufferGetHeight(pixelBufferRef)
|
|
cropWidth:CVPixelBufferGetWidth(pixelBufferRef) - cropX
|
|
cropHeight:CVPixelBufferGetHeight(pixelBufferRef) - cropY
|
|
cropX:cropX
|
|
cropY:cropY];
|
|
|
|
XCTAssertEqual(buffer.width, 720);
|
|
XCTAssertEqual(buffer.height, 1280);
|
|
|
|
CVPixelBufferRef outputPixelBufferRef = NULL;
|
|
CVPixelBufferCreate(NULL, 360, 640, pixelFormat, NULL, &outputPixelBufferRef);
|
|
[buffer cropAndScaleTo:outputPixelBufferRef withTempBuffer:NULL];
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *scaledBuffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:outputPixelBufferRef];
|
|
XCTAssertEqual(scaledBuffer.width, 360);
|
|
XCTAssertEqual(scaledBuffer.height, 640);
|
|
|
|
RTC_OBJC_TYPE(RTCI420Buffer) *originalBufferI420 = [buffer toI420];
|
|
RTC_OBJC_TYPE(RTCI420Buffer) *scaledBufferI420 = [scaledBuffer toI420];
|
|
double psnr =
|
|
I420PSNR(*[originalBufferI420 nativeI420Buffer], *[scaledBufferI420 nativeI420Buffer]);
|
|
XCTAssertEqual(psnr, webrtc::kPerfectPSNR);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
- (void)toI420WithPixelFormat:(OSType)pixelFormat {
|
|
rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = CreateI420Gradient(360, 640);
|
|
|
|
CVPixelBufferRef pixelBufferRef = NULL;
|
|
CVPixelBufferCreate(NULL, 360, 640, pixelFormat, NULL, &pixelBufferRef);
|
|
|
|
CopyI420BufferToCVPixelBuffer(i420Buffer, pixelBufferRef);
|
|
|
|
RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer =
|
|
[[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBufferRef];
|
|
RTC_OBJC_TYPE(RTCI420Buffer) *fromCVPixelBuffer = [buffer toI420];
|
|
|
|
double psnr = I420PSNR(*i420Buffer, *[fromCVPixelBuffer nativeI420Buffer]);
|
|
double target = webrtc::kPerfectPSNR;
|
|
if (pixelFormat != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
|
|
// libyuv's I420ToRGB functions seem to lose some quality.
|
|
target = 19.0;
|
|
}
|
|
XCTAssertGreaterThanOrEqual(psnr, target);
|
|
|
|
CVBufferRelease(pixelBufferRef);
|
|
}
|
|
|
|
@end
|