i210_buffer.cc (7562B)
1 /* 2 * Copyright (c) 2022 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/i210_buffer.h" 11 12 #include <cstdint> 13 #include <utility> 14 15 #include "api/make_ref_counted.h" 16 #include "api/scoped_refptr.h" 17 #include "api/video/i420_buffer.h" 18 #include "api/video/i422_buffer.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 static const int kBytesPerPixel = 2; 32 33 namespace webrtc { 34 35 namespace { 36 37 int I210DataSize(int width, 38 int height, 39 int stride_y, 40 int stride_u, 41 int stride_v) { 42 CheckValidDimensions(width, height, stride_y, stride_u, stride_v); 43 int64_t h = height, y = stride_y, u = stride_u, v = stride_v; 44 return checked_cast<int>(kBytesPerPixel * (y * h + u * h + v * h)); 45 } 46 47 } // namespace 48 49 I210Buffer::I210Buffer(int width, 50 int height, 51 int stride_y, 52 int stride_u, 53 int stride_v) 54 : width_(width), 55 height_(height), 56 stride_y_(stride_y), 57 stride_u_(stride_u), 58 stride_v_(stride_v), 59 data_(static_cast<uint16_t*>(AlignedMalloc( 60 I210DataSize(width, height, stride_y, stride_u, stride_v), 61 kBufferAlignment))) { 62 RTC_DCHECK_GE(stride_u, (width + 1) / 2); 63 RTC_DCHECK_GE(stride_v, (width + 1) / 2); 64 } 65 66 I210Buffer::~I210Buffer() {} 67 68 // static 69 scoped_refptr<I210Buffer> I210Buffer::Create(int width, int height) { 70 return make_ref_counted<I210Buffer>(width, height, width, (width + 1) / 2, 71 (width + 1) / 2); 72 } 73 74 // static 75 scoped_refptr<I210Buffer> I210Buffer::Copy(const I210BufferInterface& source) { 76 const int width = source.width(); 77 const int height = source.height(); 78 scoped_refptr<I210Buffer> buffer = Create(width, height); 79 RTC_CHECK_EQ( 80 0, libyuv::I210Copy( 81 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(), 82 source.DataV(), source.StrideV(), buffer->MutableDataY(), 83 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 84 buffer->MutableDataV(), buffer->StrideV(), width, height)); 85 return buffer; 86 } 87 88 // static 89 scoped_refptr<I210Buffer> I210Buffer::Copy(const I420BufferInterface& source) { 90 const int width = source.width(); 91 const int height = source.height(); 92 auto i422buffer = I422Buffer::Copy(source); 93 scoped_refptr<I210Buffer> buffer = Create(width, height); 94 RTC_CHECK_EQ(0, libyuv::I422ToI210(i422buffer->DataY(), i422buffer->StrideY(), 95 i422buffer->DataU(), i422buffer->StrideU(), 96 i422buffer->DataV(), i422buffer->StrideV(), 97 buffer->MutableDataY(), buffer->StrideY(), 98 buffer->MutableDataU(), buffer->StrideU(), 99 buffer->MutableDataV(), buffer->StrideV(), 100 width, height)); 101 return buffer; 102 } 103 104 // static 105 scoped_refptr<I210Buffer> I210Buffer::Rotate(const I210BufferInterface& src, 106 VideoRotation rotation) { 107 RTC_CHECK(src.DataY()); 108 RTC_CHECK(src.DataU()); 109 RTC_CHECK(src.DataV()); 110 111 int rotated_width = src.width(); 112 int rotated_height = src.height(); 113 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 114 std::swap(rotated_width, rotated_height); 115 } 116 117 scoped_refptr<I210Buffer> buffer = 118 I210Buffer::Create(rotated_width, rotated_height); 119 120 RTC_CHECK_EQ(0, 121 libyuv::I210Rotate( 122 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), 123 src.DataV(), src.StrideV(), buffer->MutableDataY(), 124 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 125 buffer->MutableDataV(), buffer->StrideV(), src.width(), 126 src.height(), static_cast<libyuv::RotationMode>(rotation))); 127 128 return buffer; 129 } 130 131 scoped_refptr<I420BufferInterface> I210Buffer::ToI420() { 132 scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(width(), height()); 133 libyuv::I210ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), 134 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 135 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 136 i420_buffer->MutableDataV(), i420_buffer->StrideV(), 137 width(), height()); 138 return i420_buffer; 139 } 140 141 int I210Buffer::width() const { 142 return width_; 143 } 144 145 int I210Buffer::height() const { 146 return height_; 147 } 148 149 const uint16_t* I210Buffer::DataY() const { 150 return data_.get(); 151 } 152 const uint16_t* I210Buffer::DataU() const { 153 return data_.get() + stride_y_ * height_; 154 } 155 const uint16_t* I210Buffer::DataV() const { 156 return data_.get() + stride_y_ * height_ + stride_u_ * height_; 157 } 158 159 int I210Buffer::StrideY() const { 160 return stride_y_; 161 } 162 int I210Buffer::StrideU() const { 163 return stride_u_; 164 } 165 int I210Buffer::StrideV() const { 166 return stride_v_; 167 } 168 169 uint16_t* I210Buffer::MutableDataY() { 170 return const_cast<uint16_t*>(DataY()); 171 } 172 uint16_t* I210Buffer::MutableDataU() { 173 return const_cast<uint16_t*>(DataU()); 174 } 175 uint16_t* I210Buffer::MutableDataV() { 176 return const_cast<uint16_t*>(DataV()); 177 } 178 179 void I210Buffer::CropAndScaleFrom(const I210BufferInterface& src, 180 int offset_x, 181 int offset_y, 182 int crop_width, 183 int crop_height) { 184 RTC_CHECK_LE(crop_width, src.width()); 185 RTC_CHECK_LE(crop_height, src.height()); 186 RTC_CHECK_LE(crop_width + offset_x, src.width()); 187 RTC_CHECK_LE(crop_height + offset_y, src.height()); 188 RTC_CHECK_GE(offset_x, 0); 189 RTC_CHECK_GE(offset_y, 0); 190 RTC_CHECK_GE(crop_width, 0); 191 RTC_CHECK_GE(crop_height, 0); 192 193 // Make sure offset is even so that u/v plane becomes aligned. 194 const int uv_offset_x = offset_x / 2; 195 const int uv_offset_y = offset_y; 196 offset_x = uv_offset_x * 2; 197 198 const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 199 const uint16_t* u_plane = 200 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x; 201 const uint16_t* v_plane = 202 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x; 203 int res = libyuv::I422Scale_16( 204 y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, src.StrideV(), 205 crop_width, crop_height, MutableDataY(), StrideY(), MutableDataU(), 206 StrideU(), MutableDataV(), StrideV(), width(), height(), 207 libyuv::kFilterBox); 208 209 RTC_DCHECK_EQ(res, 0); 210 } 211 212 void I210Buffer::ScaleFrom(const I210BufferInterface& src) { 213 CropAndScaleFrom(src, 0, 0, src.width(), src.height()); 214 } 215 216 } // namespace webrtc