i410_buffer.cc (7650B)
1 /* 2 * Copyright (c) 2023 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/i410_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/planar_functions.h" 27 #include "third_party/libyuv/include/libyuv/rotate.h" 28 #include "third_party/libyuv/include/libyuv/scale.h" 29 30 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. 31 static const int kBufferAlignment = 64; 32 static const int kBytesPerPixel = 2; 33 34 namespace webrtc { 35 36 namespace { 37 38 int I410DataSize(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>(kBytesPerPixel * (y * h + u * h + v * h)); 46 } 47 48 } // namespace 49 50 I410Buffer::I410Buffer(int width, int height) 51 : I410Buffer(width, height, width, width, width) {} 52 53 I410Buffer::I410Buffer(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<uint16_t*>(AlignedMalloc( 64 I410DataSize(width, height, stride_y, stride_u, stride_v), 65 kBufferAlignment))) { 66 RTC_DCHECK_GE(stride_u, width); 67 RTC_DCHECK_GE(stride_v, width); 68 } 69 70 I410Buffer::~I410Buffer() {} 71 72 // static 73 scoped_refptr<I410Buffer> I410Buffer::Create(int width, int height) { 74 return make_ref_counted<I410Buffer>(width, height); 75 } 76 77 // static 78 scoped_refptr<I410Buffer> I410Buffer::Create(int width, 79 int height, 80 int stride_y, 81 int stride_u, 82 int stride_v) { 83 return make_ref_counted<I410Buffer>(width, height, stride_y, stride_u, 84 stride_v); 85 } 86 87 // static 88 scoped_refptr<I410Buffer> I410Buffer::Copy(const I410BufferInterface& 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<I410Buffer> I410Buffer::Copy(int width, 96 int height, 97 const uint16_t* data_y, 98 int stride_y, 99 const uint16_t* data_u, 100 int stride_u, 101 const uint16_t* data_v, 102 int stride_v) { 103 // Note: May use different strides than the input data. 104 scoped_refptr<I410Buffer> buffer = Create(width, height); 105 int res = libyuv::I410Copy(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 RTC_DCHECK_EQ(res, 0); 111 112 return buffer; 113 } 114 115 // static 116 scoped_refptr<I410Buffer> I410Buffer::Rotate(const I410BufferInterface& src, 117 VideoRotation rotation) { 118 RTC_CHECK(src.DataY()); 119 RTC_CHECK(src.DataU()); 120 RTC_CHECK(src.DataV()); 121 122 int rotated_width = src.width(); 123 int rotated_height = src.height(); 124 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 125 std::swap(rotated_width, rotated_height); 126 } 127 128 scoped_refptr<I410Buffer> buffer = 129 I410Buffer::Create(rotated_width, rotated_height); 130 131 int res = libyuv::I410Rotate( 132 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), src.DataV(), 133 src.StrideV(), buffer->MutableDataY(), buffer->StrideY(), 134 buffer->MutableDataU(), buffer->StrideU(), buffer->MutableDataV(), 135 buffer->StrideV(), src.width(), src.height(), 136 static_cast<libyuv::RotationMode>(rotation)); 137 RTC_DCHECK_EQ(res, 0); 138 139 return buffer; 140 } 141 142 scoped_refptr<I420BufferInterface> I410Buffer::ToI420() { 143 scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(width(), height()); 144 int res = libyuv::I410ToI420( 145 DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), 146 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 147 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 148 i420_buffer->MutableDataV(), i420_buffer->StrideV(), width(), height()); 149 RTC_DCHECK_EQ(res, 0); 150 151 return i420_buffer; 152 } 153 154 void I410Buffer::InitializeData() { 155 memset(data_.get(), 0, 156 I410DataSize(width_, height_, stride_y_, stride_u_, stride_v_)); 157 } 158 159 int I410Buffer::width() const { 160 return width_; 161 } 162 163 int I410Buffer::height() const { 164 return height_; 165 } 166 167 const uint16_t* I410Buffer::DataY() const { 168 return data_.get(); 169 } 170 const uint16_t* I410Buffer::DataU() const { 171 return data_.get() + stride_y_ * height_; 172 } 173 const uint16_t* I410Buffer::DataV() const { 174 return data_.get() + stride_y_ * height_ + stride_u_ * height_; 175 } 176 177 int I410Buffer::StrideY() const { 178 return stride_y_; 179 } 180 int I410Buffer::StrideU() const { 181 return stride_u_; 182 } 183 int I410Buffer::StrideV() const { 184 return stride_v_; 185 } 186 187 uint16_t* I410Buffer::MutableDataY() { 188 return const_cast<uint16_t*>(DataY()); 189 } 190 uint16_t* I410Buffer::MutableDataU() { 191 return const_cast<uint16_t*>(DataU()); 192 } 193 uint16_t* I410Buffer::MutableDataV() { 194 return const_cast<uint16_t*>(DataV()); 195 } 196 197 void I410Buffer::CropAndScaleFrom(const I410BufferInterface& src, 198 int offset_x, 199 int offset_y, 200 int crop_width, 201 int crop_height) { 202 RTC_CHECK_LE(crop_width, src.width()); 203 RTC_CHECK_LE(crop_height, src.height()); 204 RTC_CHECK_LE(crop_width + offset_x, src.width()); 205 RTC_CHECK_LE(crop_height + offset_y, src.height()); 206 RTC_CHECK_GE(offset_x, 0); 207 RTC_CHECK_GE(offset_y, 0); 208 209 const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 210 const uint16_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; 211 const uint16_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; 212 int res = libyuv::I444Scale_16( 213 y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, src.StrideV(), 214 crop_width, crop_height, MutableDataY(), StrideY(), MutableDataU(), 215 StrideU(), MutableDataV(), StrideV(), width(), height(), 216 libyuv::kFilterBox); 217 218 RTC_DCHECK_EQ(res, 0); 219 } 220 221 void I410Buffer::ScaleFrom(const I410BufferInterface& src) { 222 CropAndScaleFrom(src, 0, 0, src.width(), src.height()); 223 } 224 225 } // namespace webrtc