i420_buffer.cc (8439B)
1 /* 2 * Copyright (c) 2015 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 #include "api/video/i420_buffer.h" 11 12 #include <algorithm> 13 #include <cstdint> 14 #include <cstring> 15 #include <utility> 16 17 #include "api/make_ref_counted.h" 18 #include "api/scoped_refptr.h" 19 #include "api/video/video_frame_buffer.h" 20 #include "api/video/video_rotation.h" 21 #include "rtc_base/checks.h" 22 #include "rtc_base/memory/aligned_malloc.h" 23 #include "rtc_base/numerics/safe_conversions.h" 24 #include "third_party/libyuv/include/libyuv/convert.h" 25 #include "third_party/libyuv/include/libyuv/planar_functions.h" 26 #include "third_party/libyuv/include/libyuv/rotate.h" 27 #include "third_party/libyuv/include/libyuv/scale.h" 28 29 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. 30 static const int kBufferAlignment = 64; 31 32 namespace webrtc { 33 34 namespace { 35 36 int I420DataSize(int width, 37 int height, 38 int stride_y, 39 int stride_u, 40 int stride_v) { 41 CheckValidDimensions(width, height, stride_y, stride_u, stride_v); 42 // Do the size calculation using 64bit integers and use checked_cast to catch 43 // overflow. 44 int64_t h = height, y = stride_y, u = stride_u, v = stride_v; 45 return checked_cast<int>(y * h + (u + v) * ((h + 1) / 2)); 46 } 47 48 } // namespace 49 50 I420Buffer::I420Buffer(int width, int height) 51 : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {} 52 53 I420Buffer::I420Buffer(int width, 54 int height, 55 int stride_y, 56 int stride_u, 57 int stride_v) 58 : width_(width), 59 height_(height), 60 stride_y_(stride_y), 61 stride_u_(stride_u), 62 stride_v_(stride_v), 63 data_(static_cast<uint8_t*>(AlignedMalloc( 64 I420DataSize(width, height, stride_y, stride_u, stride_v), 65 kBufferAlignment))) { 66 RTC_DCHECK_GE(stride_u, (width + 1) / 2); 67 RTC_DCHECK_GE(stride_v, (width + 1) / 2); 68 } 69 70 I420Buffer::~I420Buffer() {} 71 72 // static 73 scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) { 74 return make_ref_counted<I420Buffer>(width, height); 75 } 76 77 // static 78 scoped_refptr<I420Buffer> I420Buffer::Create(int width, 79 int height, 80 int stride_y, 81 int stride_u, 82 int stride_v) { 83 return make_ref_counted<I420Buffer>(width, height, stride_y, stride_u, 84 stride_v); 85 } 86 87 // static 88 scoped_refptr<I420Buffer> I420Buffer::Copy(const I420BufferInterface& source) { 89 return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), 90 source.DataU(), source.StrideU(), source.DataV(), 91 source.StrideV()); 92 } 93 94 // static 95 scoped_refptr<I420Buffer> I420Buffer::Copy(int width, 96 int height, 97 const uint8_t* data_y, 98 int stride_y, 99 const uint8_t* data_u, 100 int stride_u, 101 const uint8_t* data_v, 102 int stride_v) { 103 // Note: May use different strides than the input data. 104 scoped_refptr<I420Buffer> buffer = Create(width, height); 105 RTC_CHECK_EQ(0, libyuv::I420Copy(data_y, stride_y, data_u, stride_u, data_v, 106 stride_v, buffer->MutableDataY(), 107 buffer->StrideY(), buffer->MutableDataU(), 108 buffer->StrideU(), buffer->MutableDataV(), 109 buffer->StrideV(), width, height)); 110 return buffer; 111 } 112 113 // static 114 scoped_refptr<I420Buffer> I420Buffer::Rotate(const I420BufferInterface& src, 115 VideoRotation rotation) { 116 RTC_CHECK(src.DataY()); 117 RTC_CHECK(src.DataU()); 118 RTC_CHECK(src.DataV()); 119 120 int rotated_width = src.width(); 121 int rotated_height = src.height(); 122 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 123 std::swap(rotated_width, rotated_height); 124 } 125 126 scoped_refptr<I420Buffer> buffer = 127 I420Buffer::Create(rotated_width, rotated_height); 128 129 RTC_CHECK_EQ(0, 130 libyuv::I420Rotate( 131 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), 132 src.DataV(), src.StrideV(), buffer->MutableDataY(), 133 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 134 buffer->MutableDataV(), buffer->StrideV(), src.width(), 135 src.height(), static_cast<libyuv::RotationMode>(rotation))); 136 137 return buffer; 138 } 139 140 void I420Buffer::InitializeData() { 141 memset(data_.get(), 0, 142 I420DataSize(width_, height_, stride_y_, stride_u_, stride_v_)); 143 } 144 145 int I420Buffer::width() const { 146 return width_; 147 } 148 149 int I420Buffer::height() const { 150 return height_; 151 } 152 153 const uint8_t* I420Buffer::DataY() const { 154 return data_.get(); 155 } 156 const uint8_t* I420Buffer::DataU() const { 157 return data_.get() + stride_y_ * height_; 158 } 159 const uint8_t* I420Buffer::DataV() const { 160 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2); 161 } 162 163 int I420Buffer::StrideY() const { 164 return stride_y_; 165 } 166 int I420Buffer::StrideU() const { 167 return stride_u_; 168 } 169 int I420Buffer::StrideV() const { 170 return stride_v_; 171 } 172 173 uint8_t* I420Buffer::MutableDataY() { 174 return const_cast<uint8_t*>(DataY()); 175 } 176 uint8_t* I420Buffer::MutableDataU() { 177 return const_cast<uint8_t*>(DataU()); 178 } 179 uint8_t* I420Buffer::MutableDataV() { 180 return const_cast<uint8_t*>(DataV()); 181 } 182 183 // static 184 void I420Buffer::SetBlack(I420Buffer* buffer) { 185 RTC_CHECK(libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(), 186 buffer->MutableDataU(), buffer->StrideU(), 187 buffer->MutableDataV(), buffer->StrideV(), 0, 0, 188 buffer->width(), buffer->height(), 0, 128, 189 128) == 0); 190 } 191 192 void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src, 193 int offset_x, 194 int offset_y, 195 int crop_width, 196 int crop_height) { 197 RTC_CHECK_LE(crop_width, src.width()); 198 RTC_CHECK_LE(crop_height, src.height()); 199 RTC_CHECK_LE(crop_width + offset_x, src.width()); 200 RTC_CHECK_LE(crop_height + offset_y, src.height()); 201 RTC_CHECK_GE(offset_x, 0); 202 RTC_CHECK_GE(offset_y, 0); 203 204 // Make sure offset is even so that u/v plane becomes aligned. 205 const int uv_offset_x = offset_x / 2; 206 const int uv_offset_y = offset_y / 2; 207 offset_x = uv_offset_x * 2; 208 offset_y = uv_offset_y * 2; 209 210 const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 211 const uint8_t* u_plane = 212 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x; 213 const uint8_t* v_plane = 214 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x; 215 int res = 216 libyuv::I420Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, 217 src.StrideV(), crop_width, crop_height, MutableDataY(), 218 StrideY(), MutableDataU(), StrideU(), MutableDataV(), 219 StrideV(), width(), height(), libyuv::kFilterBox); 220 221 RTC_DCHECK_EQ(res, 0); 222 } 223 224 void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src) { 225 const int crop_width = 226 height() > 0 ? std::min(src.width(), width() * src.height() / height()) 227 : src.width(); 228 const int crop_height = 229 width() > 0 ? std::min(src.height(), height() * src.width() / width()) 230 : src.height(); 231 232 CropAndScaleFrom(src, (src.width() - crop_width) / 2, 233 (src.height() - crop_height) / 2, crop_width, crop_height); 234 } 235 236 void I420Buffer::ScaleFrom(const I420BufferInterface& src) { 237 CropAndScaleFrom(src, 0, 0, src.width(), src.height()); 238 } 239 240 } // namespace webrtc