blob: 203896758e833fe6dc6b83e81305d82bcee882ba [file] [log] [blame]
Anders Carlssone5960ce2017-06-22 15:26:30 +02001/*
2 * Copyright 2017 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 "WebRTC/RTCVideoFrameBuffer.h"
12
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "common_video/libyuv/include/webrtc_libyuv.h"
14#include "rtc_base/checks.h"
15#include "rtc_base/logging.h"
Anders Carlssone5960ce2017-06-22 15:26:30 +020016
Mirko Bonadei65432062017-12-11 09:32:13 +010017#include "third_party/libyuv/include/libyuv.h"
Anders Carlssonf3ee3b72017-10-23 15:23:00 +020018
Anders Carlssone5960ce2017-06-22 15:26:30 +020019@implementation RTCCVPixelBuffer {
20 int _width;
21 int _height;
22 int _bufferWidth;
23 int _bufferHeight;
24 int _cropWidth;
25 int _cropHeight;
Anders Carlssone5960ce2017-06-22 15:26:30 +020026}
27
28@synthesize pixelBuffer = _pixelBuffer;
andersc151aa6b2017-08-25 01:33:18 -070029@synthesize cropX = _cropX;
30@synthesize cropY = _cropY;
Anders Carlssone5960ce2017-06-22 15:26:30 +020031
Anders Carlssonf3ee3b72017-10-23 15:23:00 +020032+ (NSSet<NSNumber*>*)supportedPixelFormats {
33 return [NSSet setWithObjects:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),
34 @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange),
35 @(kCVPixelFormatType_32BGRA),
36 @(kCVPixelFormatType_32ARGB),
37 nil];
38}
39
Anders Carlssone5960ce2017-06-22 15:26:30 +020040- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer {
41 return [self initWithPixelBuffer:pixelBuffer
42 adaptedWidth:CVPixelBufferGetWidth(pixelBuffer)
43 adaptedHeight:CVPixelBufferGetHeight(pixelBuffer)
44 cropWidth:CVPixelBufferGetWidth(pixelBuffer)
45 cropHeight:CVPixelBufferGetHeight(pixelBuffer)
46 cropX:0
47 cropY:0];
48}
49
50- (instancetype)initWithPixelBuffer:(CVPixelBufferRef)pixelBuffer
51 adaptedWidth:(int)adaptedWidth
52 adaptedHeight:(int)adaptedHeight
53 cropWidth:(int)cropWidth
54 cropHeight:(int)cropHeight
55 cropX:(int)cropX
56 cropY:(int)cropY {
57 if (self = [super init]) {
58 _width = adaptedWidth;
59 _height = adaptedHeight;
60 _pixelBuffer = pixelBuffer;
61 _bufferWidth = CVPixelBufferGetWidth(_pixelBuffer);
62 _bufferHeight = CVPixelBufferGetHeight(_pixelBuffer);
63 _cropWidth = cropWidth;
64 _cropHeight = cropHeight;
65 // Can only crop at even pixels.
66 _cropX = cropX & ~1;
67 _cropY = cropY & ~1;
68 CVBufferRetain(_pixelBuffer);
69 }
70
71 return self;
72}
73
74- (void)dealloc {
75 CVBufferRelease(_pixelBuffer);
76}
77
78- (int)width {
79 return _width;
80}
81
82- (int)height {
83 return _height;
84}
85
86- (BOOL)requiresCropping {
87 return _cropWidth != _bufferWidth || _cropHeight != _bufferHeight;
88}
89
90- (BOOL)requiresScalingToWidth:(int)width height:(int)height {
91 return _cropWidth != width || _cropHeight != height;
92}
93
94- (int)bufferSizeForCroppingAndScalingToWidth:(int)width height:(int)height {
Anders Carlssonf3ee3b72017-10-23 15:23:00 +020095 const OSType srcPixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer);
96 switch (srcPixelFormat) {
97 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
98 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: {
99 int srcChromaWidth = (_cropWidth + 1) / 2;
100 int srcChromaHeight = (_cropHeight + 1) / 2;
101 int dstChromaWidth = (width + 1) / 2;
102 int dstChromaHeight = (height + 1) / 2;
Anders Carlssone5960ce2017-06-22 15:26:30 +0200103
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200104 return srcChromaWidth * srcChromaHeight * 2 + dstChromaWidth * dstChromaHeight * 2;
105 }
106 case kCVPixelFormatType_32BGRA:
107 case kCVPixelFormatType_32ARGB: {
108 return 0; // Scaling RGBA frames does not require a temporary buffer.
109 }
110 }
111 RTC_NOTREACHED() << "Unsupported pixel format.";
112 return 0;
Anders Carlssone5960ce2017-06-22 15:26:30 +0200113}
114
115- (BOOL)cropAndScaleTo:(CVPixelBufferRef)outputPixelBuffer withTempBuffer:(uint8_t*)tmpBuffer {
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200116 const OSType srcPixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer);
117 switch (srcPixelFormat) {
118 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
119 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: {
120 [self cropAndScaleNV12To:outputPixelBuffer withTempBuffer:tmpBuffer];
121 break;
122 }
123 case kCVPixelFormatType_32BGRA:
124 case kCVPixelFormatType_32ARGB: {
125 [self cropAndScaleARGBTo:outputPixelBuffer];
126 break;
127 }
128 default: { RTC_NOTREACHED() << "Unsupported pixel format."; }
129 }
130
131 return YES;
132}
133
134- (id<RTCI420Buffer>)toI420 {
135 const OSType pixelFormat = CVPixelBufferGetPixelFormatType(_pixelBuffer);
136
137 CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
138
139 RTCMutableI420Buffer* i420Buffer =
140 [[RTCMutableI420Buffer alloc] initWithWidth:[self width] height:[self height]];
141
142 switch (pixelFormat) {
143 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
144 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: {
145 const uint8_t* srcY =
146 static_cast<const uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 0));
147 const int srcYStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 0);
148 const uint8_t* srcUV =
149 static_cast<const uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 1));
150 const int srcUVStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 1);
151
152 // Crop just by modifying pointers.
153 srcY += srcYStride * _cropY + _cropX;
154 srcUV += srcUVStride * (_cropY / 2) + _cropX;
155
156 // TODO(magjed): Use a frame buffer pool.
157 webrtc::NV12ToI420Scaler nv12ToI420Scaler;
158 nv12ToI420Scaler.NV12ToI420Scale(srcY,
159 srcYStride,
160 srcUV,
161 srcUVStride,
162 _cropWidth,
163 _cropHeight,
164 i420Buffer.mutableDataY,
165 i420Buffer.strideY,
166 i420Buffer.mutableDataU,
167 i420Buffer.strideU,
168 i420Buffer.mutableDataV,
169 i420Buffer.strideV,
170 i420Buffer.width,
171 i420Buffer.height);
172 break;
173 }
174 case kCVPixelFormatType_32BGRA:
175 case kCVPixelFormatType_32ARGB: {
176 const uint8_t* src =
177 static_cast<const uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 0));
178
179 uint32 libyuvPixelFormat = 0;
180 if (pixelFormat == kCVPixelFormatType_32BGRA) {
181 libyuvPixelFormat = libyuv::FOURCC_ARGB;
182 } else if (pixelFormat == kCVPixelFormatType_32ARGB) {
183 libyuvPixelFormat = libyuv::FOURCC_ABGR;
184 }
185
186 libyuv::ConvertToI420(src,
187 0,
188 i420Buffer.mutableDataY,
189 i420Buffer.strideY,
190 i420Buffer.mutableDataU,
191 i420Buffer.strideU,
192 i420Buffer.mutableDataV,
193 i420Buffer.strideV,
194 _cropX,
195 _cropY,
196 _cropWidth,
197 _cropHeight,
198 i420Buffer.width,
199 i420Buffer.height,
200 libyuv::kRotate0,
201 libyuvPixelFormat);
202 break;
203 }
204 default: { RTC_NOTREACHED() << "Unsupported pixel format."; }
205 }
206
207 CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
208
209 return i420Buffer;
210}
211
212- (void)cropAndScaleNV12To:(CVPixelBufferRef)outputPixelBuffer withTempBuffer:(uint8_t*)tmpBuffer {
Anders Carlssone5960ce2017-06-22 15:26:30 +0200213 // Prepare output pointers.
Anders Carlssone5960ce2017-06-22 15:26:30 +0200214 CVReturn cvRet = CVPixelBufferLockBaseAddress(outputPixelBuffer, 0);
215 if (cvRet != kCVReturnSuccess) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100216 RTC_LOG(LS_ERROR) << "Failed to lock base address: " << cvRet;
Anders Carlssone5960ce2017-06-22 15:26:30 +0200217 }
218 const int dstWidth = CVPixelBufferGetWidth(outputPixelBuffer);
219 const int dstHeight = CVPixelBufferGetHeight(outputPixelBuffer);
220 uint8_t* dstY =
221 reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(outputPixelBuffer, 0));
222 const int dstYStride = CVPixelBufferGetBytesPerRowOfPlane(outputPixelBuffer, 0);
223 uint8_t* dstUV =
224 reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(outputPixelBuffer, 1));
225 const int dstUVStride = CVPixelBufferGetBytesPerRowOfPlane(outputPixelBuffer, 1);
226
227 // Prepare source pointers.
Anders Carlssone5960ce2017-06-22 15:26:30 +0200228 CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
229 const uint8_t* srcY =
230 static_cast<const uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 0));
231 const int srcYStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 0);
232 const uint8_t* srcUV =
233 static_cast<const uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(_pixelBuffer, 1));
234 const int srcUVStride = CVPixelBufferGetBytesPerRowOfPlane(_pixelBuffer, 1);
235
236 // Crop just by modifying pointers.
237 srcY += srcYStride * _cropY + _cropX;
238 srcUV += srcUVStride * (_cropY / 2) + _cropX;
239
240 webrtc::NV12Scale(tmpBuffer,
241 srcY,
242 srcYStride,
243 srcUV,
244 srcUVStride,
245 _cropWidth,
246 _cropHeight,
247 dstY,
248 dstYStride,
249 dstUV,
250 dstUVStride,
251 dstWidth,
252 dstHeight);
253
254 CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
255 CVPixelBufferUnlockBaseAddress(outputPixelBuffer, 0);
Anders Carlssone5960ce2017-06-22 15:26:30 +0200256}
257
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200258- (void)cropAndScaleARGBTo:(CVPixelBufferRef)outputPixelBuffer {
259 // Prepare output pointers.
260 CVReturn cvRet = CVPixelBufferLockBaseAddress(outputPixelBuffer, 0);
261 if (cvRet != kCVReturnSuccess) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100262 RTC_LOG(LS_ERROR) << "Failed to lock base address: " << cvRet;
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200263 }
264 const int dstWidth = CVPixelBufferGetWidth(outputPixelBuffer);
265 const int dstHeight = CVPixelBufferGetHeight(outputPixelBuffer);
Anders Carlssone5960ce2017-06-22 15:26:30 +0200266
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200267 uint8_t* dst =
268 reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(outputPixelBuffer, 0));
269 const int dstStride = CVPixelBufferGetBytesPerRowOfPlane(outputPixelBuffer, 0);
270
271 // Prepare source pointers.
Anders Carlssone5960ce2017-06-22 15:26:30 +0200272 CVPixelBufferLockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200273 const uint8_t* src = static_cast<const uint8_t*>(CVPixelBufferGetBaseAddress(_pixelBuffer));
274 const int srcStride = CVPixelBufferGetBytesPerRow(_pixelBuffer);
Anders Carlssone5960ce2017-06-22 15:26:30 +0200275
276 // Crop just by modifying pointers.
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200277 src += srcStride * _cropY + _cropX;
278 libyuv::ARGBScale(src,
279 srcStride,
280 _cropWidth,
281 _cropHeight,
282 dst,
283 dstStride,
284 dstWidth,
285 dstHeight,
286 libyuv::kFilterBox);
Anders Carlssone5960ce2017-06-22 15:26:30 +0200287
288 CVPixelBufferUnlockBaseAddress(_pixelBuffer, kCVPixelBufferLock_ReadOnly);
Anders Carlssonf3ee3b72017-10-23 15:23:00 +0200289 CVPixelBufferUnlockBaseAddress(outputPixelBuffer, 0);
Anders Carlssone5960ce2017-06-22 15:26:30 +0200290}
291
292@end