i010_buffer.cc (7201B)
1 /* 2 * Copyright (c) 2018 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/i010_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/video_frame_buffer.h" 19 #include "api/video/video_rotation.h" 20 #include "rtc_base/checks.h" 21 #include "rtc_base/memory/aligned_malloc.h" 22 #include "rtc_base/numerics/safe_conversions.h" 23 #include "third_party/libyuv/include/libyuv/convert.h" 24 #include "third_party/libyuv/include/libyuv/convert_from.h" 25 #include "third_party/libyuv/include/libyuv/rotate.h" 26 #include "third_party/libyuv/include/libyuv/scale.h" 27 28 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. 29 static const int kBufferAlignment = 64; 30 static const int kBytesPerPixel = 2; 31 32 namespace webrtc { 33 34 namespace { 35 36 int I010DataSize(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 int64_t h = height, y = stride_y, u = stride_u, v = stride_v; 43 return checked_cast<int>(kBytesPerPixel * (y * h + (u + v) * ((h + 1) / 2))); 44 } 45 46 } // namespace 47 48 I010Buffer::I010Buffer(int width, 49 int height, 50 int stride_y, 51 int stride_u, 52 int stride_v) 53 : width_(width), 54 height_(height), 55 stride_y_(stride_y), 56 stride_u_(stride_u), 57 stride_v_(stride_v), 58 data_(static_cast<uint16_t*>(AlignedMalloc( 59 I010DataSize(width, height, stride_y, stride_u, stride_v), 60 kBufferAlignment))) { 61 RTC_DCHECK_GE(stride_u, (width + 1) / 2); 62 RTC_DCHECK_GE(stride_v, (width + 1) / 2); 63 } 64 65 I010Buffer::~I010Buffer() {} 66 67 // static 68 scoped_refptr<I010Buffer> I010Buffer::Create(int width, int height) { 69 return make_ref_counted<I010Buffer>(width, height, width, (width + 1) / 2, 70 (width + 1) / 2); 71 } 72 73 // static 74 scoped_refptr<I010Buffer> I010Buffer::Copy(const I010BufferInterface& source) { 75 const int width = source.width(); 76 const int height = source.height(); 77 scoped_refptr<I010Buffer> buffer = Create(width, height); 78 int res = libyuv::I010Copy( 79 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(), 80 source.DataV(), source.StrideV(), buffer->MutableDataY(), 81 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 82 buffer->MutableDataV(), buffer->StrideV(), width, height); 83 RTC_DCHECK_EQ(res, 0); 84 85 return buffer; 86 } 87 88 // static 89 scoped_refptr<I010Buffer> I010Buffer::Copy(const I420BufferInterface& source) { 90 const int width = source.width(); 91 const int height = source.height(); 92 scoped_refptr<I010Buffer> buffer = Create(width, height); 93 int res = libyuv::I420ToI010( 94 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(), 95 source.DataV(), source.StrideV(), buffer->MutableDataY(), 96 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), 97 buffer->MutableDataV(), buffer->StrideV(), width, height); 98 RTC_DCHECK_EQ(res, 0); 99 100 return buffer; 101 } 102 103 // static 104 scoped_refptr<I010Buffer> I010Buffer::Rotate(const I010BufferInterface& src, 105 VideoRotation rotation) { 106 if (rotation == kVideoRotation_0) 107 return Copy(src); 108 109 RTC_CHECK(src.DataY()); 110 RTC_CHECK(src.DataU()); 111 RTC_CHECK(src.DataV()); 112 int rotated_width = src.width(); 113 int rotated_height = src.height(); 114 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 115 std::swap(rotated_width, rotated_height); 116 } 117 118 scoped_refptr<I010Buffer> buffer = Create(rotated_width, rotated_height); 119 120 int res = libyuv::I010Rotate( 121 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), src.DataV(), 122 src.StrideV(), buffer->MutableDataY(), buffer->StrideY(), 123 buffer->MutableDataU(), buffer->StrideU(), buffer->MutableDataV(), 124 buffer->StrideV(), src.width(), src.height(), 125 static_cast<libyuv::RotationMode>(rotation)); 126 RTC_DCHECK_EQ(res, 0); 127 128 return buffer; 129 } 130 131 scoped_refptr<I420BufferInterface> I010Buffer::ToI420() { 132 scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(width(), height()); 133 int res = libyuv::I010ToI420( 134 DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), 135 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 136 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 137 i420_buffer->MutableDataV(), i420_buffer->StrideV(), width(), height()); 138 RTC_DCHECK_EQ(res, 0); 139 140 return i420_buffer; 141 } 142 143 int I010Buffer::width() const { 144 return width_; 145 } 146 147 int I010Buffer::height() const { 148 return height_; 149 } 150 151 const uint16_t* I010Buffer::DataY() const { 152 return data_.get(); 153 } 154 const uint16_t* I010Buffer::DataU() const { 155 return data_.get() + stride_y_ * height_; 156 } 157 const uint16_t* I010Buffer::DataV() const { 158 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2); 159 } 160 161 int I010Buffer::StrideY() const { 162 return stride_y_; 163 } 164 int I010Buffer::StrideU() const { 165 return stride_u_; 166 } 167 int I010Buffer::StrideV() const { 168 return stride_v_; 169 } 170 171 uint16_t* I010Buffer::MutableDataY() { 172 return const_cast<uint16_t*>(DataY()); 173 } 174 uint16_t* I010Buffer::MutableDataU() { 175 return const_cast<uint16_t*>(DataU()); 176 } 177 uint16_t* I010Buffer::MutableDataV() { 178 return const_cast<uint16_t*>(DataV()); 179 } 180 181 void I010Buffer::CropAndScaleFrom(const I010BufferInterface& src, 182 int offset_x, 183 int offset_y, 184 int crop_width, 185 int crop_height) { 186 RTC_CHECK_LE(crop_width, src.width()); 187 RTC_CHECK_LE(crop_height, src.height()); 188 RTC_CHECK_LE(crop_width + offset_x, src.width()); 189 RTC_CHECK_LE(crop_height + offset_y, src.height()); 190 RTC_CHECK_GE(offset_x, 0); 191 RTC_CHECK_GE(offset_y, 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 / 2; 196 offset_x = uv_offset_x * 2; 197 offset_y = uv_offset_y * 2; 198 199 const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 200 const uint16_t* u_plane = 201 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x; 202 const uint16_t* v_plane = 203 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x; 204 int res = libyuv::I420Scale_16( 205 y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, src.StrideV(), 206 crop_width, crop_height, MutableDataY(), StrideY(), MutableDataU(), 207 StrideU(), MutableDataV(), StrideV(), width(), height(), 208 libyuv::kFilterBox); 209 210 RTC_DCHECK_EQ(res, 0); 211 } 212 213 void I010Buffer::ScaleFrom(const I010BufferInterface& src) { 214 CropAndScaleFrom(src, 0, 0, src.width(), src.height()); 215 } 216 217 } // namespace webrtc