nv12_buffer.cc (5219B)
1 /* 2 * Copyright (c) 2020 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 11 #include "api/video/nv12_buffer.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <cstring> 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 "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/convert_from.h" 26 #include "third_party/libyuv/include/libyuv/scale.h" 27 28 namespace webrtc { 29 30 namespace { 31 32 const int kBufferAlignment = 64; 33 34 int NV12DataSize(int width, int height, int stride_y, int stride_uv) { 35 CheckValidDimensions(width, height, stride_y, stride_uv, stride_uv); 36 int64_t h = height, y = stride_y, uv = stride_uv; 37 return checked_cast<int>(y * h + uv * ((h + 1) / 2)); 38 } 39 40 } // namespace 41 42 NV12Buffer::NV12Buffer(int width, int height) 43 : NV12Buffer(width, height, width, width + width % 2) {} 44 45 NV12Buffer::NV12Buffer(int width, int height, int stride_y, int stride_uv) 46 : width_(width), 47 height_(height), 48 stride_y_(stride_y), 49 stride_uv_(stride_uv), 50 data_(static_cast<uint8_t*>( 51 AlignedMalloc(NV12DataSize(width, height, stride_y, stride_uv), 52 kBufferAlignment))) { 53 RTC_DCHECK_GE(stride_uv, width + width % 2); 54 } 55 56 NV12Buffer::~NV12Buffer() = default; 57 58 // static 59 scoped_refptr<NV12Buffer> NV12Buffer::Create(int width, int height) { 60 return make_ref_counted<NV12Buffer>(width, height); 61 } 62 63 // static 64 scoped_refptr<NV12Buffer> NV12Buffer::Create(int width, 65 int height, 66 int stride_y, 67 int stride_uv) { 68 return make_ref_counted<NV12Buffer>(width, height, stride_y, stride_uv); 69 } 70 71 // static 72 scoped_refptr<NV12Buffer> NV12Buffer::Copy( 73 const I420BufferInterface& i420_buffer) { 74 scoped_refptr<NV12Buffer> buffer = 75 NV12Buffer::Create(i420_buffer.width(), i420_buffer.height()); 76 libyuv::I420ToNV12( 77 i420_buffer.DataY(), i420_buffer.StrideY(), i420_buffer.DataU(), 78 i420_buffer.StrideU(), i420_buffer.DataV(), i420_buffer.StrideV(), 79 buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataUV(), 80 buffer->StrideUV(), buffer->width(), buffer->height()); 81 return buffer; 82 } 83 84 scoped_refptr<I420BufferInterface> NV12Buffer::ToI420() { 85 scoped_refptr<I420Buffer> i420_buffer = I420Buffer::Create(width(), height()); 86 libyuv::NV12ToI420(DataY(), StrideY(), DataUV(), StrideUV(), 87 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 88 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 89 i420_buffer->MutableDataV(), i420_buffer->StrideV(), 90 width(), height()); 91 return i420_buffer; 92 } 93 94 int NV12Buffer::width() const { 95 return width_; 96 } 97 int NV12Buffer::height() const { 98 return height_; 99 } 100 101 int NV12Buffer::StrideY() const { 102 return stride_y_; 103 } 104 int NV12Buffer::StrideUV() const { 105 return stride_uv_; 106 } 107 108 const uint8_t* NV12Buffer::DataY() const { 109 return data_.get(); 110 } 111 112 const uint8_t* NV12Buffer::DataUV() const { 113 return data_.get() + UVOffset(); 114 } 115 116 uint8_t* NV12Buffer::MutableDataY() { 117 return data_.get(); 118 } 119 120 uint8_t* NV12Buffer::MutableDataUV() { 121 return data_.get() + UVOffset(); 122 } 123 124 size_t NV12Buffer::UVOffset() const { 125 return stride_y_ * height_; 126 } 127 128 void NV12Buffer::InitializeData() { 129 memset(data_.get(), 0, NV12DataSize(width_, height_, stride_y_, stride_uv_)); 130 } 131 132 void NV12Buffer::CropAndScaleFrom(const NV12BufferInterface& src, 133 int offset_x, 134 int offset_y, 135 int crop_width, 136 int crop_height) { 137 RTC_CHECK_LE(crop_width, src.width()); 138 RTC_CHECK_LE(crop_height, src.height()); 139 RTC_CHECK_LE(crop_width + offset_x, src.width()); 140 RTC_CHECK_LE(crop_height + offset_y, src.height()); 141 RTC_CHECK_GE(offset_x, 0); 142 RTC_CHECK_GE(offset_y, 0); 143 144 // Make sure offset is even so that u/v plane becomes aligned. 145 const int uv_offset_x = offset_x / 2; 146 const int uv_offset_y = offset_y / 2; 147 offset_x = uv_offset_x * 2; 148 offset_y = uv_offset_y * 2; 149 150 const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; 151 const uint8_t* uv_plane = 152 src.DataUV() + src.StrideUV() * uv_offset_y + uv_offset_x * 2; 153 154 int res = libyuv::NV12Scale(y_plane, src.StrideY(), uv_plane, src.StrideUV(), 155 crop_width, crop_height, MutableDataY(), 156 StrideY(), MutableDataUV(), StrideUV(), width(), 157 height(), libyuv::kFilterBox); 158 159 RTC_DCHECK_EQ(res, 0); 160 } 161 162 } // namespace webrtc