| /* | |
| * Copyright (c) 2013 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 "modules/desktop_capture/desktop_frame.h" | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <utility> | |
| #include "modules/desktop_capture/desktop_capture_types.h" | |
| #include "modules/desktop_capture/desktop_geometry.h" | |
| #include "modules/desktop_capture/shared_memory.h" | |
| #include "rtc_base/checks.h" | |
| #include "third_party/libyuv/include/libyuv/planar_functions.h" | |
| namespace webrtc { | |
| DesktopFrame::DesktopFrame(DesktopSize size, | |
| int stride, | |
| FourCC pixel_format, | |
| uint8_t* data, | |
| SharedMemory* shared_memory, | |
| FrameTexture* texture) | |
| : data_(data), | |
| shared_memory_(shared_memory), | |
| texture_(texture), | |
| size_(size), | |
| stride_(stride), | |
| pixel_format_(pixel_format), | |
| capture_time_ms_(0), | |
| capturer_id_(DesktopCapturerId::kUnknown) { | |
| RTC_DCHECK(size_.width() >= 0); | |
| RTC_DCHECK(size_.height() >= 0); | |
| } | |
| DesktopFrame::DesktopFrame(DesktopSize size, | |
| int stride, | |
| uint8_t* data, | |
| SharedMemory* shared_memory) | |
| : DesktopFrame(size, stride, FOURCC_ARGB, data, shared_memory) {} | |
| DesktopFrame::~DesktopFrame() = default; | |
| void DesktopFrame::CopyPixelsFrom(const uint8_t* src_buffer, | |
| int src_stride, | |
| const DesktopRect& dest_rect) { | |
| RTC_CHECK(DesktopRect::MakeSize(size()).ContainsRect(dest_rect)); | |
| uint8_t* dest = GetFrameDataAtPos(dest_rect.top_left()); | |
| libyuv::CopyPlane(src_buffer, src_stride, dest, stride(), | |
| DesktopFrame::kBytesPerPixel * dest_rect.width(), | |
| dest_rect.height()); | |
| } | |
| void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame, | |
| const DesktopVector& src_pos, | |
| const DesktopRect& dest_rect) { | |
| RTC_CHECK(DesktopRect::MakeSize(src_frame.size()) | |
| .ContainsRect( | |
| DesktopRect::MakeOriginSize(src_pos, dest_rect.size()))); | |
| RTC_CHECK_EQ(pixel_format(), src_frame.pixel_format()); | |
| CopyPixelsFrom(src_frame.GetFrameDataAtPos(src_pos), src_frame.stride(), | |
| dest_rect); | |
| } | |
| bool DesktopFrame::CopyIntersectingPixelsFrom(const DesktopFrame& src_frame, | |
| double horizontal_scale, | |
| double vertical_scale) { | |
| const DesktopVector& origin = top_left(); | |
| const DesktopVector& src_frame_origin = src_frame.top_left(); | |
| DesktopVector src_frame_offset = src_frame_origin.subtract(origin); | |
| // Determine the intersection, first adjusting its origin to account for any | |
| // DPI scaling. | |
| DesktopRect intersection_rect = src_frame.rect(); | |
| if (horizontal_scale != 1.0 || vertical_scale != 1.0) { | |
| DesktopVector origin_adjustment( | |
| static_cast<int>( | |
| std::round((horizontal_scale - 1.0) * src_frame_offset.x())), | |
| static_cast<int>( | |
| std::round((vertical_scale - 1.0) * src_frame_offset.y()))); | |
| intersection_rect.Translate(origin_adjustment); | |
| src_frame_offset = src_frame_offset.add(origin_adjustment); | |
| } | |
| intersection_rect.IntersectWith(rect()); | |
| if (intersection_rect.is_empty()) { | |
| return false; | |
| } | |
| // Translate the intersection rect to be relative to the outer rect. | |
| intersection_rect.Translate(-origin.x(), -origin.y()); | |
| // Determine source position for the copy (offsets of outer frame from | |
| // source origin, if positive). | |
| int32_t src_pos_x = std::max(0, -src_frame_offset.x()); | |
| int32_t src_pos_y = std::max(0, -src_frame_offset.y()); | |
| CopyPixelsFrom(src_frame, DesktopVector(src_pos_x, src_pos_y), | |
| intersection_rect); | |
| return true; | |
| } | |
| DesktopRect DesktopFrame::rect() const { | |
| const float scale = scale_factor(); | |
| // Only scale the size. | |
| return DesktopRect::MakeXYWH(top_left().x(), top_left().y(), | |
| size().width() / scale, size().height() / scale); | |
| } | |
| float DesktopFrame::scale_factor() const { | |
| float scale = 1.0f; | |
| #if defined(WEBRTC_MAC) || defined(CHROMEOS) | |
| // At least on Windows the logical and physical pixel are the same | |
| // See http://crbug.com/948362. | |
| if (!dpi().is_zero() && dpi().x() == dpi().y()) | |
| scale = dpi().x() / kStandardDPI; | |
| #endif | |
| return scale; | |
| } | |
| uint8_t* DesktopFrame::GetFrameDataAtPos(const DesktopVector& pos) const { | |
| RTC_DCHECK(data()); | |
| return data() + stride() * pos.y() + DesktopFrame::kBytesPerPixel * pos.x(); | |
| } | |
| void DesktopFrame::CopyFrameInfoFrom(const DesktopFrame& other) { | |
| set_dpi(other.dpi()); | |
| set_capture_time_ms(other.capture_time_ms()); | |
| set_capturer_id(other.capturer_id()); | |
| *mutable_updated_region() = other.updated_region(); | |
| set_top_left(other.top_left()); | |
| set_icc_profile(other.icc_profile()); | |
| set_may_contain_cursor(other.may_contain_cursor()); | |
| set_device_scale_factor(other.device_scale_factor()); | |
| } | |
| void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) { | |
| set_dpi(other->dpi()); | |
| set_capture_time_ms(other->capture_time_ms()); | |
| set_capturer_id(other->capturer_id()); | |
| mutable_updated_region()->Swap(other->mutable_updated_region()); | |
| set_top_left(other->top_left()); | |
| set_icc_profile(other->icc_profile()); | |
| set_may_contain_cursor(other->may_contain_cursor()); | |
| set_device_scale_factor(other->device_scale_factor()); | |
| } | |
| bool DesktopFrame::FrameDataIsBlack() const { | |
| RTC_DCHECK(data()); | |
| if (size().is_empty()) | |
| return false; | |
| uint32_t* pixel = reinterpret_cast<uint32_t*>(data()); | |
| for (int i = 0; i < size().width() * size().height(); ++i) { | |
| if (*pixel++) | |
| return false; | |
| } | |
| return true; | |
| } | |
| void DesktopFrame::SetFrameDataToBlack() { | |
| RTC_DCHECK(data()); | |
| const uint8_t kBlackPixelValue = 0x00; | |
| memset(data(), kBlackPixelValue, stride() * size().height()); | |
| } | |
| BasicDesktopFrame::BasicDesktopFrame(DesktopSize size) | |
| : BasicDesktopFrame(size, FOURCC_ARGB) {} | |
| BasicDesktopFrame::BasicDesktopFrame(DesktopSize size, FourCC pixel_format) | |
| : DesktopFrame(size, | |
| kBytesPerPixel * size.width(), | |
| pixel_format, | |
| new uint8_t[kBytesPerPixel * size.width() * size.height()](), | |
| nullptr) {} | |
| BasicDesktopFrame::~BasicDesktopFrame() { | |
| delete[] data_; | |
| } | |
| // static | |
| DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) { | |
| DesktopFrame* result = | |
| new BasicDesktopFrame(frame.size(), frame.pixel_format()); | |
| // TODO(crbug.com/1330019): Temporary workaround for a known libyuv crash when | |
| // the height or width is 0. Remove this once this change has been merged. | |
| if (frame.size().width() && frame.size().height()) { | |
| libyuv::CopyPlane(frame.data(), frame.stride(), result->data(), | |
| result->stride(), frame.size().width() * kBytesPerPixel, | |
| frame.size().height()); | |
| } | |
| result->CopyFrameInfoFrom(frame); | |
| return result; | |
| } | |
| // static | |
| std::unique_ptr<DesktopFrame> SharedMemoryDesktopFrame::Create( | |
| DesktopSize size, | |
| FourCC pixel_format, | |
| SharedMemoryFactory* shared_memory_factory) { | |
| RTC_DCHECK(shared_memory_factory); | |
| size_t buffer_size = size.height() * size.width() * kBytesPerPixel; | |
| std::unique_ptr<SharedMemory> shared_memory = | |
| shared_memory_factory->CreateSharedMemory(buffer_size); | |
| if (!shared_memory) | |
| return nullptr; | |
| return std::make_unique<SharedMemoryDesktopFrame>( | |
| size, size.width() * kBytesPerPixel, pixel_format, | |
| std::move(shared_memory)); | |
| } | |
| SharedMemoryDesktopFrame::SharedMemoryDesktopFrame( | |
| DesktopSize size, | |
| int stride, | |
| std::unique_ptr<SharedMemory> shared_memory) | |
| : SharedMemoryDesktopFrame(size, | |
| stride, | |
| FOURCC_ARGB, | |
| std::move(shared_memory)) {} | |
| SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(DesktopSize size, | |
| int stride, | |
| FourCC pixel_format, | |
| SharedMemory* shared_memory) | |
| : DesktopFrame(size, | |
| stride, | |
| pixel_format, | |
| reinterpret_cast<uint8_t*>(shared_memory->data()), | |
| shared_memory) {} | |
| SharedMemoryDesktopFrame::SharedMemoryDesktopFrame( | |
| DesktopSize size, | |
| int stride, | |
| FourCC pixel_format, | |
| std::unique_ptr<SharedMemory> shared_memory) | |
| : SharedMemoryDesktopFrame(size, | |
| stride, | |
| pixel_format, | |
| shared_memory.release()) {} | |
| SharedMemoryDesktopFrame::~SharedMemoryDesktopFrame() { | |
| delete shared_memory_; | |
| } | |
| } // namespace webrtc |