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