i444_buffer.cc (7604B)
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/i444_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 33 namespace webrtc { 34 35 namespace { 36 37 int I444DataSize(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>(y * h + u * h + v * h); 45 } 46 47 } // namespace 48 49 I444Buffer::I444Buffer(int width, int height) 50 : I444Buffer(width, height, width, (width), (width)) {} 51 52 I444Buffer::I444Buffer(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 I444DataSize(width, height, stride_y, stride_u, stride_v), 64 kBufferAlignment))) { 65 RTC_DCHECK_GE(stride_u, width); 66 RTC_DCHECK_GE(stride_v, width); 67 } 68 69 I444Buffer::~I444Buffer() {} 70 71 // static 72 scoped_refptr<I444Buffer> I444Buffer::Create(int width, int height) { 73 return make_ref_counted<I444Buffer>(width, height); 74 } 75 76 // static 77 scoped_refptr<I444Buffer> I444Buffer::Create(int width, 78 int height, 79 int stride_y, 80 int stride_u, 81 int stride_v) { 82 return make_ref_counted<I444Buffer>(width, height, stride_y, stride_u, 83 stride_v); 84 } 85 86 // static 87 scoped_refptr<I444Buffer> I444Buffer::Copy(const I444BufferInterface& 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<I444Buffer> I444Buffer::Copy(int width, 95 int height, 96 const uint8_t* data_y, 97 int stride_y, 98 const uint8_t* data_u, 99 int stride_u, 100 const uint8_t* data_v, 101 int stride_v) { 102 // Note: May use different strides than the input data. 103 scoped_refptr<I444Buffer> buffer = Create(width, height); 104 RTC_CHECK_EQ(0, libyuv::I444Copy(data_y, stride_y, data_u, stride_u, data_v, 105 stride_v, buffer->MutableDataY(), 106 buffer->StrideY(), buffer->MutableDataU(), 107 buffer->StrideU(), buffer->MutableDataV(), 108 buffer->StrideV(), width, height)); 109 return buffer; 110 } 111 112 // static 113 scoped_refptr<I444Buffer> I444Buffer::Rotate(const I444BufferInterface& src, 114 VideoRotation rotation) { 115 RTC_CHECK(src.DataY()); 116 RTC_CHECK(src.DataU()); 117 RTC_CHECK(src.DataV()); 118 119 int rotated_width = src.width(); 120 int rotated_height = src.height(); 121 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 122 std::swap(rotated_width, rotated_height); 123 } 124 125 scoped_refptr<I444Buffer> buffer = 126 I444Buffer::Create(rotated_width, rotated_height); 127 128 RTC_CHECK_EQ(0, 129 libyuv::I444Rotate( 130 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), 131 src.DataV(), src.StrideV(), buffer->MutableDataY(), 132 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 133 buffer->MutableDataV(), buffer->StrideV(), src.width(), 134 src.height(), static_cast<libyuv::RotationMode>(rotation))); 135 136 return buffer; 137 } 138 139 scoped_refptr<I420BufferInterface> I444Buffer::ToI420() { 140 scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(width(), height()); 141 libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), 142 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 143 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 144 i420_buffer->MutableDataV(), i420_buffer->StrideV(), 145 width(), height()); 146 return i420_buffer; 147 } 148 149 void I444Buffer::InitializeData() { 150 memset(data_.get(), 0, 151 I444DataSize(width_, height_, stride_y_, stride_u_, stride_v_)); 152 } 153 154 int I444Buffer::width() const { 155 return width_; 156 } 157 158 int I444Buffer::height() const { 159 return height_; 160 } 161 162 const uint8_t* I444Buffer::DataY() const { 163 return data_.get(); 164 } 165 const uint8_t* I444Buffer::DataU() const { 166 return data_.get() + stride_y_ * height_; 167 } 168 const uint8_t* I444Buffer::DataV() const { 169 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_)); 170 } 171 172 int I444Buffer::StrideY() const { 173 return stride_y_; 174 } 175 int I444Buffer::StrideU() const { 176 return stride_u_; 177 } 178 int I444Buffer::StrideV() const { 179 return stride_v_; 180 } 181 182 uint8_t* I444Buffer::MutableDataY() { 183 return const_cast<uint8_t*>(DataY()); 184 } 185 uint8_t* I444Buffer::MutableDataU() { 186 return const_cast<uint8_t*>(DataU()); 187 } 188 uint8_t* I444Buffer::MutableDataV() { 189 return const_cast<uint8_t*>(DataV()); 190 } 191 192 void I444Buffer::CropAndScaleFrom(const I444BufferInterface& 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 const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 205 const uint8_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; 206 const uint8_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; 207 int res = 208 libyuv::I444Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, 209 src.StrideV(), crop_width, crop_height, MutableDataY(), 210 StrideY(), MutableDataU(), StrideU(), MutableDataV(), 211 StrideV(), width(), height(), libyuv::kFilterBox); 212 213 RTC_DCHECK_EQ(res, 0); 214 } 215 216 } // namespace webrtc