| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/ash/services/recording/rgb_video_frame.h" |
| |
| #include <cstring> |
| |
| #include "base/check_op.h" |
| #include "base/compiler_specific.h" |
| #include "third_party/skia/include/core/SkColorType.h" |
| |
| namespace recording { |
| |
| namespace { |
| |
| // Wraps the given `video_frame` pixels in a bitmap and returns it. Note that |
| // this does not copy the pixels bytes from `video_frame` to the returned |
| // bitmap, and hence the bitmap is safe to access as long as the `video_frame` |
| // is alive. |
| SkBitmap WrapVideoFrameInBitmap(const media::VideoFrame& video_frame) { |
| const gfx::Size visible_size = video_frame.visible_rect().size(); |
| const SkImageInfo image_info = SkImageInfo::MakeN32( |
| visible_size.width(), visible_size.height(), kPremul_SkAlphaType, |
| video_frame.ColorSpace().ToSkColorSpace()); |
| |
| SkBitmap bitmap; |
| const uint8_t* pixels = |
| video_frame.visible_data(media::VideoFrame::Plane::kARGB); |
| bitmap.installPixels( |
| SkPixmap(image_info, pixels, |
| video_frame.row_bytes(media::VideoFrame::Plane::kARGB))); |
| return bitmap; |
| } |
| |
| } // namespace |
| |
| RgbVideoFrame::RgbVideoFrame(const media::VideoFrame& video_frame) |
| : RgbVideoFrame(WrapVideoFrameInBitmap(video_frame), |
| video_frame.metadata().reference_time.value_or( |
| base::TimeTicks::Now())) {} |
| |
| RgbVideoFrame::RgbVideoFrame(const SkBitmap& bitmap, base::TimeTicks frame_time) |
| : width_(bitmap.width()), |
| height_(bitmap.height()), |
| frame_time_(frame_time), |
| data_(new RgbColor[width_ * height_]) { |
| DCHECK_EQ(kN32_SkColorType, bitmap.colorType()); |
| |
| const size_t bytes_per_pixel = bitmap.bytesPerPixel(); |
| DCHECK_EQ(bytes_per_pixel, sizeof(RgbColor)); |
| |
| // Some bitmaps may contain padding pixels at the end of each row. In this |
| // case the returned value of `rowBytesAsPixels()` will be larger than |
| // `width_`. If there are no padding pixels, then we can copy the whole buffer |
| // in a 1-shot `memcpy()` call. |
| if (width_ == bitmap.rowBytesAsPixels()) { |
| const size_t num_bytes = num_pixels() * bytes_per_pixel; |
| UNSAFE_TODO(std::memcpy(&data_[0], bitmap.getPixels(), num_bytes)); |
| return; |
| } |
| |
| // Otherwise, we have to copy it row by row. |
| const size_t num_pixels_per_row = width_; |
| const size_t bytes_per_row = num_pixels_per_row * bytes_per_pixel; |
| |
| DCHECK_EQ(num_pixels() * sizeof(RgbColor), bytes_per_row * height_); |
| DCHECK_EQ(width_ * sizeof(RgbColor), bytes_per_row); |
| |
| for (int row = 0; row < height_; ++row) { |
| UNSAFE_TODO(std::memcpy(&data_[row * width_], bitmap.getAddr(0, row), |
| bytes_per_row)); |
| } |
| } |
| |
| RgbVideoFrame::RgbVideoFrame(RgbVideoFrame&&) = default; |
| |
| RgbVideoFrame::~RgbVideoFrame() = default; |
| |
| RgbVideoFrame RgbVideoFrame::Clone() const { |
| return RgbVideoFrame(*this); |
| } |
| |
| RgbVideoFrame::RgbVideoFrame(const RgbVideoFrame& other) |
| : width_(other.width_), |
| height_(other.height_), |
| data_(new RgbColor[width_ * height_]) { |
| UNSAFE_TODO( |
| std::memcpy(&data_[0], &other.data_[0], num_pixels() * sizeof(RgbColor))); |
| } |
| |
| } // namespace recording |