diff --git a/api/video/i444_buffer.cc b/api/video/i444_buffer.cc index 5f8b714636..92f1662f43 100644 --- a/api/video/i444_buffer.cc +++ b/api/video/i444_buffer.cc @@ -1,211 +1,211 @@ -/* - * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "api/video/i444_buffer.h" - -#include - -#include -#include - -#include "api/video/i420_buffer.h" -#include "rtc_base/checks.h" -#include "rtc_base/ref_counted_object.h" -#include "third_party/libyuv/include/libyuv/convert.h" -#include "third_party/libyuv/include/libyuv/planar_functions.h" -#include "third_party/libyuv/include/libyuv/scale.h" - -// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. -static const int kBufferAlignment = 64; - -namespace webrtc { - -namespace { - -int I444DataSize(int height, int stride_y, int stride_u, int stride_v) { - return stride_y * height + stride_u * height + stride_v * height; -} - -} // namespace - -I444Buffer::I444Buffer(int width, int height) - : I444Buffer(width, height, width, (width), (width)) {} - -I444Buffer::I444Buffer(int width, - int height, - int stride_y, - int stride_u, - int stride_v) - : width_(width), - height_(height), - stride_y_(stride_y), - stride_u_(stride_u), - stride_v_(stride_v), - data_(static_cast( - AlignedMalloc(I444DataSize(height, stride_y, stride_u, stride_v), - kBufferAlignment))) { - RTC_DCHECK_GT(width, 0); - RTC_DCHECK_GT(height, 0); - RTC_DCHECK_GE(stride_y, width); - RTC_DCHECK_GE(stride_u, (width)); - RTC_DCHECK_GE(stride_v, (width)); -} - -I444Buffer::~I444Buffer() {} - -// static -rtc::scoped_refptr I444Buffer::Create(int width, int height) { - return new rtc::RefCountedObject(width, height); -} - -// static -rtc::scoped_refptr I444Buffer::Create(int width, - int height, - int stride_y, - int stride_u, - int stride_v) { - return new rtc::RefCountedObject(width, height, stride_y, - stride_u, stride_v); -} - -// static -rtc::scoped_refptr I444Buffer::Copy( - const I444BufferInterface& source) { - return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), - source.DataU(), source.StrideU(), source.DataV(), - source.StrideV()); -} - -// static -rtc::scoped_refptr I444Buffer::Copy(int width, - int height, - const uint8_t* data_y, - int stride_y, - const uint8_t* data_u, - int stride_u, - const uint8_t* data_v, - int stride_v) { - // Note: May use different strides than the input data. - rtc::scoped_refptr buffer = Create(width, height); - RTC_CHECK_EQ(0, libyuv::I444Copy(data_y, stride_y, data_u, stride_u, data_v, - stride_v, buffer->MutableDataY(), - buffer->StrideY(), buffer->MutableDataU(), - buffer->StrideU(), buffer->MutableDataV(), - buffer->StrideV(), width, height)); - return buffer; -} - -// static -rtc::scoped_refptr I444Buffer::Rotate( - const I444BufferInterface& src, - VideoRotation rotation) { - RTC_CHECK(src.DataY()); - RTC_CHECK(src.DataU()); - RTC_CHECK(src.DataV()); - - int rotated_width = src.width(); - int rotated_height = src.height(); - if (rotation == webrtc::kVideoRotation_90 || - rotation == webrtc::kVideoRotation_270) { - std::swap(rotated_width, rotated_height); - } - - rtc::scoped_refptr buffer = - I444Buffer::Create(rotated_width, rotated_height); - - RTC_CHECK_EQ(0, - libyuv::I444Rotate( - src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), - src.DataV(), src.StrideV(), buffer->MutableDataY(), - buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), - buffer->MutableDataV(), buffer->StrideV(), src.width(), - src.height(), static_cast(rotation))); - - return buffer; -} - -rtc::scoped_refptr I444Buffer::ToI420() { - rtc::scoped_refptr i420_buffer = - I420Buffer::Create(width(), height()); - libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), - i420_buffer->MutableDataY(), i420_buffer->StrideY(), - i420_buffer->MutableDataU(), i420_buffer->StrideU(), - i420_buffer->MutableDataV(), i420_buffer->StrideV(), - width(), height()); - return i420_buffer; -} - -void I444Buffer::InitializeData() { - memset(data_.get(), 0, - I444DataSize(height_, stride_y_, stride_u_, stride_v_)); -} - -int I444Buffer::width() const { - return width_; -} - -int I444Buffer::height() const { - return height_; -} - -const uint8_t* I444Buffer::DataY() const { - return data_.get(); -} -const uint8_t* I444Buffer::DataU() const { - return data_.get() + stride_y_ * height_; -} -const uint8_t* I444Buffer::DataV() const { - return data_.get() + stride_y_ * height_ + stride_u_ * ((height_)); -} - -int I444Buffer::StrideY() const { - return stride_y_; -} -int I444Buffer::StrideU() const { - return stride_u_; -} -int I444Buffer::StrideV() const { - return stride_v_; -} - -uint8_t* I444Buffer::MutableDataY() { - return const_cast(DataY()); -} -uint8_t* I444Buffer::MutableDataU() { - return const_cast(DataU()); -} -uint8_t* I444Buffer::MutableDataV() { - return const_cast(DataV()); -} - -void I444Buffer::CropAndScaleFrom(const I444BufferInterface& src, - int offset_x, - int offset_y, - int crop_width, - int crop_height) { - RTC_CHECK_LE(crop_width, src.width()); - RTC_CHECK_LE(crop_height, src.height()); - RTC_CHECK_LE(crop_width + offset_x, src.width()); - RTC_CHECK_LE(crop_height + offset_y, src.height()); - RTC_CHECK_GE(offset_x, 0); - RTC_CHECK_GE(offset_y, 0); - - const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; - const uint8_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; - const uint8_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; - int res = - libyuv::I444Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, - src.StrideV(), crop_width, crop_height, MutableDataY(), - StrideY(), MutableDataU(), StrideU(), MutableDataV(), - StrideV(), width(), height(), libyuv::kFilterBox); - - RTC_DCHECK_EQ(res, 0); -} - -} // namespace webrtc +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/video/i444_buffer.h" + +#include + +#include +#include + +#include "api/video/i420_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/planar_functions.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. +static const int kBufferAlignment = 64; + +namespace webrtc { + +namespace { + +int I444DataSize(int height, int stride_y, int stride_u, int stride_v) { + return stride_y * height + stride_u * height + stride_v * height; +} + +} // namespace + +I444Buffer::I444Buffer(int width, int height) + : I444Buffer(width, height, width, (width), (width)) {} + +I444Buffer::I444Buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) + : width_(width), + height_(height), + stride_y_(stride_y), + stride_u_(stride_u), + stride_v_(stride_v), + data_(static_cast( + AlignedMalloc(I444DataSize(height, stride_y, stride_u, stride_v), + kBufferAlignment))) { + RTC_DCHECK_GT(width, 0); + RTC_DCHECK_GT(height, 0); + RTC_DCHECK_GE(stride_y, width); + RTC_DCHECK_GE(stride_u, (width)); + RTC_DCHECK_GE(stride_v, (width)); +} + +I444Buffer::~I444Buffer() {} + +// static +rtc::scoped_refptr I444Buffer::Create(int width, int height) { + return new rtc::RefCountedObject(width, height); +} + +// static +rtc::scoped_refptr I444Buffer::Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return new rtc::RefCountedObject(width, height, stride_y, + stride_u, stride_v); +} + +// static +rtc::scoped_refptr I444Buffer::Copy( + const I444BufferInterface& source) { + return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), + source.DataU(), source.StrideU(), source.DataV(), + source.StrideV()); +} + +// static +rtc::scoped_refptr I444Buffer::Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v) { + // Note: May use different strides than the input data. + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ(0, libyuv::I444Copy(data_y, stride_y, data_u, stride_u, data_v, + stride_v, buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), + buffer->StrideU(), buffer->MutableDataV(), + buffer->StrideV(), width, height)); + return buffer; +} + +// static +rtc::scoped_refptr I444Buffer::Rotate( + const I444BufferInterface& src, + VideoRotation rotation) { + RTC_CHECK(src.DataY()); + RTC_CHECK(src.DataU()); + RTC_CHECK(src.DataV()); + + int rotated_width = src.width(); + int rotated_height = src.height(); + if (rotation == webrtc::kVideoRotation_90 || + rotation == webrtc::kVideoRotation_270) { + std::swap(rotated_width, rotated_height); + } + + rtc::scoped_refptr buffer = + I444Buffer::Create(rotated_width, rotated_height); + + RTC_CHECK_EQ(0, + libyuv::I444Rotate( + src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), + src.DataV(), src.StrideV(), buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), src.width(), + src.height(), static_cast(rotation))); + + return buffer; +} + +rtc::scoped_refptr I444Buffer::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), + i420_buffer->MutableDataY(), i420_buffer->StrideY(), + i420_buffer->MutableDataU(), i420_buffer->StrideU(), + i420_buffer->MutableDataV(), i420_buffer->StrideV(), + width(), height()); + return i420_buffer; +} + +void I444Buffer::InitializeData() { + memset(data_.get(), 0, + I444DataSize(height_, stride_y_, stride_u_, stride_v_)); +} + +int I444Buffer::width() const { + return width_; +} + +int I444Buffer::height() const { + return height_; +} + +const uint8_t* I444Buffer::DataY() const { + return data_.get(); +} +const uint8_t* I444Buffer::DataU() const { + return data_.get() + stride_y_ * height_; +} +const uint8_t* I444Buffer::DataV() const { + return data_.get() + stride_y_ * height_ + stride_u_ * ((height_)); +} + +int I444Buffer::StrideY() const { + return stride_y_; +} +int I444Buffer::StrideU() const { + return stride_u_; +} +int I444Buffer::StrideV() const { + return stride_v_; +} + +uint8_t* I444Buffer::MutableDataY() { + return const_cast(DataY()); +} +uint8_t* I444Buffer::MutableDataU() { + return const_cast(DataU()); +} +uint8_t* I444Buffer::MutableDataV() { + return const_cast(DataV()); +} + +void I444Buffer::CropAndScaleFrom(const I444BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height) { + RTC_CHECK_LE(crop_width, src.width()); + RTC_CHECK_LE(crop_height, src.height()); + RTC_CHECK_LE(crop_width + offset_x, src.width()); + RTC_CHECK_LE(crop_height + offset_y, src.height()); + RTC_CHECK_GE(offset_x, 0); + RTC_CHECK_GE(offset_y, 0); + + const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; + const uint8_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; + const uint8_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; + int res = + libyuv::I444Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, + src.StrideV(), crop_width, crop_height, MutableDataY(), + StrideY(), MutableDataU(), StrideU(), MutableDataV(), + StrideV(), width(), height(), libyuv::kFilterBox); + + RTC_DCHECK_EQ(res, 0); +} + +} // namespace webrtc diff --git a/api/video/i444_buffer.h b/api/video/i444_buffer.h index d46c2454ca..557bf4f3e0 100644 --- a/api/video/i444_buffer.h +++ b/api/video/i444_buffer.h @@ -1,104 +1,104 @@ -/* - * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_VIDEO_I444_BUFFER_H_ -#define API_VIDEO_I444_BUFFER_H_ - -#include - -#include - -#include "api/scoped_refptr.h" -#include "api/video/video_frame_buffer.h" -#include "api/video/video_rotation.h" -#include "rtc_base/memory/aligned_malloc.h" -#include "rtc_base/system/rtc_export.h" - -namespace webrtc { - -// Plain I444 buffer in standard memory. -// I444 represents an image with in YUV format withouth any chroma subsampling. -// https://en.wikipedia.org/wiki/Chroma_subsampling#4:4:4 -class RTC_EXPORT I444Buffer : public I444BufferInterface { - public: - static rtc::scoped_refptr Create(int width, int height); - static rtc::scoped_refptr Create(int width, - int height, - int stride_y, - int stride_u, - int stride_v); - - // Create a new buffer and copy the pixel data. - static rtc::scoped_refptr Copy(const I444BufferInterface& buffer); - - static rtc::scoped_refptr Copy(int width, - int height, - const uint8_t* data_y, - int stride_y, - const uint8_t* data_u, - int stride_u, - const uint8_t* data_v, - int stride_v); - - // Returns a rotated copy of |src|. - static rtc::scoped_refptr Rotate(const I444BufferInterface& src, - VideoRotation rotation); - - rtc::scoped_refptr ToI420() final; - const I420BufferInterface* GetI420() const final { return nullptr; } - - // Sets all three planes to all zeros. Used to work around for - // quirks in memory checkers - // (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and - // ffmpeg (http://crbug.com/390941). - // TODO(nisse): Deprecated. Should be deleted if/when those issues - // are resolved in a better way. Or in the mean time, use SetBlack. - void InitializeData(); - - int width() const override; - int height() const override; - const uint8_t* DataY() const override; - const uint8_t* DataU() const override; - const uint8_t* DataV() const override; - - int StrideY() const override; - int StrideU() const override; - int StrideV() const override; - - uint8_t* MutableDataY(); - uint8_t* MutableDataU(); - uint8_t* MutableDataV(); - - // Scale the cropped area of |src| to the size of |this| buffer, and - // write the result into |this|. - void CropAndScaleFrom(const I444BufferInterface& src, - int offset_x, - int offset_y, - int crop_width, - int crop_height); - - protected: - I444Buffer(int width, int height); - I444Buffer(int width, int height, int stride_y, int stride_u, int stride_v); - - ~I444Buffer() override; - - private: - const int width_; - const int height_; - const int stride_y_; - const int stride_u_; - const int stride_v_; - const std::unique_ptr data_; -}; - -} // namespace webrtc - -#endif // API_VIDEO_I444_BUFFER_H_ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_VIDEO_I444_BUFFER_H_ +#define API_VIDEO_I444_BUFFER_H_ + +#include + +#include + +#include "api/scoped_refptr.h" +#include "api/video/video_frame_buffer.h" +#include "api/video/video_rotation.h" +#include "rtc_base/memory/aligned_malloc.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Plain I444 buffer in standard memory. +// I444 represents an image with in YUV format withouth any chroma subsampling. +// https://en.wikipedia.org/wiki/Chroma_subsampling#4:4:4 +class RTC_EXPORT I444Buffer : public I444BufferInterface { + public: + static rtc::scoped_refptr Create(int width, int height); + static rtc::scoped_refptr Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + // Create a new buffer and copy the pixel data. + static rtc::scoped_refptr Copy(const I444BufferInterface& buffer); + + static rtc::scoped_refptr Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v); + + // Returns a rotated copy of |src|. + static rtc::scoped_refptr Rotate(const I444BufferInterface& src, + VideoRotation rotation); + + rtc::scoped_refptr ToI420() final; + const I420BufferInterface* GetI420() const final { return nullptr; } + + // Sets all three planes to all zeros. Used to work around for + // quirks in memory checkers + // (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and + // ffmpeg (http://crbug.com/390941). + // TODO(nisse): Deprecated. Should be deleted if/when those issues + // are resolved in a better way. Or in the mean time, use SetBlack. + void InitializeData(); + + int width() const override; + int height() const override; + const uint8_t* DataY() const override; + const uint8_t* DataU() const override; + const uint8_t* DataV() const override; + + int StrideY() const override; + int StrideU() const override; + int StrideV() const override; + + uint8_t* MutableDataY(); + uint8_t* MutableDataU(); + uint8_t* MutableDataV(); + + // Scale the cropped area of |src| to the size of |this| buffer, and + // write the result into |this|. + void CropAndScaleFrom(const I444BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height); + + protected: + I444Buffer(int width, int height); + I444Buffer(int width, int height, int stride_y, int stride_u, int stride_v); + + ~I444Buffer() override; + + private: + const int width_; + const int height_; + const int stride_y_; + const int stride_u_; + const int stride_v_; + const std::unique_ptr data_; +}; + +} // namespace webrtc + +#endif // API_VIDEO_I444_BUFFER_H_ diff --git a/api/video/test/i444_buffer_unittest.cc b/api/video/test/i444_buffer_unittest.cc index 52ce49ac92..9a1a9315aa 100644 --- a/api/video/test/i444_buffer_unittest.cc +++ b/api/video/test/i444_buffer_unittest.cc @@ -1,112 +1,112 @@ - -/* - * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/video/i444_buffer.h" - -#include "api/video/i420_buffer.h" -#include "test/frame_utils.h" -#include "test/gmock.h" -#include "test/gtest.h" - -namespace webrtc { - -namespace { -int GetY(rtc::scoped_refptr buf, int col, int row) { - return buf->DataY()[row * buf->StrideY() + col]; -} - -int GetU(rtc::scoped_refptr buf, int col, int row) { - return buf->DataU()[row * buf->StrideU() + col]; -} - -int GetV(rtc::scoped_refptr buf, int col, int row) { - return buf->DataV()[row * buf->StrideV() + col]; -} - -void FillI444Buffer(rtc::scoped_refptr buf) { - const uint8_t Y = 1; - const uint8_t U = 2; - const uint8_t V = 3; - for (int row = 0; row < buf->height(); ++row) { - for (int col = 0; col < buf->width(); ++col) { - buf->MutableDataY()[row * buf->StrideY() + col] = Y; - buf->MutableDataU()[row * buf->StrideU() + col] = U; - buf->MutableDataV()[row * buf->StrideV() + col] = V; - } - } -} - -} // namespace - -TEST(I444BufferTest, InitialData) { - constexpr int stride = 3; - constexpr int width = 3; - constexpr int height = 3; - - rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); - EXPECT_EQ(width, i444_buffer->width()); - EXPECT_EQ(height, i444_buffer->height()); - EXPECT_EQ(stride, i444_buffer->StrideY()); - EXPECT_EQ(stride, i444_buffer->StrideU()); - EXPECT_EQ(stride, i444_buffer->StrideV()); - EXPECT_EQ(3, i444_buffer->ChromaWidth()); - EXPECT_EQ(3, i444_buffer->ChromaHeight()); -} - -TEST(I444BufferTest, ReadPixels) { - constexpr int width = 3; - constexpr int height = 3; - - rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); - // Y = 1, U = 2, V = 3. - FillI444Buffer(i444_buffer); - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - EXPECT_EQ(1, GetY(i444_buffer, col, row)); - EXPECT_EQ(2, GetU(i444_buffer, col, row)); - EXPECT_EQ(3, GetV(i444_buffer, col, row)); - } - } -} - -TEST(I444BufferTest, ToI420) { - constexpr int width = 3; - constexpr int height = 3; - constexpr int size_y = width * height; - constexpr int size_u = (width + 1) / 2 * (height + 1) / 2; - constexpr int size_v = (width + 1) / 2 * (height + 1) / 2; - rtc::scoped_refptr reference(I420Buffer::Create(width, height)); - memset(reference->MutableDataY(), 8, size_y); - memset(reference->MutableDataU(), 4, size_u); - memset(reference->MutableDataV(), 2, size_v); - - rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); - // Convert the reference buffer to I444. - memset(i444_buffer->MutableDataY(), 8, size_y); - memset(i444_buffer->MutableDataU(), 4, size_y); - memset(i444_buffer->MutableDataV(), 2, size_y); - - // Confirm YUV values are as expected. - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - EXPECT_EQ(8, GetY(i444_buffer, col, row)); - EXPECT_EQ(4, GetU(i444_buffer, col, row)); - EXPECT_EQ(2, GetV(i444_buffer, col, row)); - } - } - - rtc::scoped_refptr i420_buffer(i444_buffer->ToI420()); - EXPECT_EQ(height, i420_buffer->height()); - EXPECT_EQ(width, i420_buffer->width()); - EXPECT_TRUE(test::FrameBufsEqual(reference, i420_buffer)); -} - -} // namespace webrtc + +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/video/i444_buffer.h" + +#include "api/video/i420_buffer.h" +#include "test/frame_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +int GetY(rtc::scoped_refptr buf, int col, int row) { + return buf->DataY()[row * buf->StrideY() + col]; +} + +int GetU(rtc::scoped_refptr buf, int col, int row) { + return buf->DataU()[row * buf->StrideU() + col]; +} + +int GetV(rtc::scoped_refptr buf, int col, int row) { + return buf->DataV()[row * buf->StrideV() + col]; +} + +void FillI444Buffer(rtc::scoped_refptr buf) { + const uint8_t Y = 1; + const uint8_t U = 2; + const uint8_t V = 3; + for (int row = 0; row < buf->height(); ++row) { + for (int col = 0; col < buf->width(); ++col) { + buf->MutableDataY()[row * buf->StrideY() + col] = Y; + buf->MutableDataU()[row * buf->StrideU() + col] = U; + buf->MutableDataV()[row * buf->StrideV() + col] = V; + } + } +} + +} // namespace + +TEST(I444BufferTest, InitialData) { + constexpr int stride = 3; + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + EXPECT_EQ(width, i444_buffer->width()); + EXPECT_EQ(height, i444_buffer->height()); + EXPECT_EQ(stride, i444_buffer->StrideY()); + EXPECT_EQ(stride, i444_buffer->StrideU()); + EXPECT_EQ(stride, i444_buffer->StrideV()); + EXPECT_EQ(3, i444_buffer->ChromaWidth()); + EXPECT_EQ(3, i444_buffer->ChromaHeight()); +} + +TEST(I444BufferTest, ReadPixels) { + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + // Y = 1, U = 2, V = 3. + FillI444Buffer(i444_buffer); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(1, GetY(i444_buffer, col, row)); + EXPECT_EQ(2, GetU(i444_buffer, col, row)); + EXPECT_EQ(3, GetV(i444_buffer, col, row)); + } + } +} + +TEST(I444BufferTest, ToI420) { + constexpr int width = 3; + constexpr int height = 3; + constexpr int size_y = width * height; + constexpr int size_u = (width + 1) / 2 * (height + 1) / 2; + constexpr int size_v = (width + 1) / 2 * (height + 1) / 2; + rtc::scoped_refptr reference(I420Buffer::Create(width, height)); + memset(reference->MutableDataY(), 8, size_y); + memset(reference->MutableDataU(), 4, size_u); + memset(reference->MutableDataV(), 2, size_v); + + rtc::scoped_refptr i444_buffer(I444Buffer::Create(width, height)); + // Convert the reference buffer to I444. + memset(i444_buffer->MutableDataY(), 8, size_y); + memset(i444_buffer->MutableDataU(), 4, size_y); + memset(i444_buffer->MutableDataV(), 2, size_y); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(8, GetY(i444_buffer, col, row)); + EXPECT_EQ(4, GetU(i444_buffer, col, row)); + EXPECT_EQ(2, GetV(i444_buffer, col, row)); + } + } + + rtc::scoped_refptr i420_buffer(i444_buffer->ToI420()); + EXPECT_EQ(height, i420_buffer->height()); + EXPECT_EQ(width, i420_buffer->width()); + EXPECT_TRUE(test::FrameBufsEqual(reference, i420_buffer)); +} + +} // namespace webrtc