| /* | |
| * Copyright (c) 2016 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_generator.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include "modules/desktop_capture/desktop_frame.h" | |
| #include "modules/desktop_capture/desktop_geometry.h" | |
| #include "modules/desktop_capture/desktop_region.h" | |
| #include "modules/desktop_capture/rgba_color.h" | |
| #include "modules/desktop_capture/shared_memory.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/random.h" | |
| #include "rtc_base/time_utils.h" | |
| namespace webrtc { | |
| namespace { | |
| // Sets `updated_region` to `frame`. If `enlarge_updated_region` is | |
| // true, this function will randomly enlarge each DesktopRect in | |
| // `updated_region`. But the enlarged DesktopRegion won't excceed the | |
| // frame->size(). If `add_random_updated_region` is true, several random | |
| // rectangles will also be included in `frame`. | |
| void SetUpdatedRegion(DesktopFrame* frame, | |
| const DesktopRegion& updated_region, | |
| bool enlarge_updated_region, | |
| int enlarge_range, | |
| bool add_random_updated_region) { | |
| const DesktopRect screen_rect = DesktopRect::MakeSize(frame->size()); | |
| Random random(TimeMicros()); | |
| frame->mutable_updated_region()->Clear(); | |
| for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | |
| it.Advance()) { | |
| DesktopRect rect = it.rect(); | |
| if (enlarge_updated_region && enlarge_range > 0) { | |
| rect.Extend(random.Rand(enlarge_range), random.Rand(enlarge_range), | |
| random.Rand(enlarge_range), random.Rand(enlarge_range)); | |
| rect.IntersectWith(screen_rect); | |
| } | |
| frame->mutable_updated_region()->AddRect(rect); | |
| } | |
| if (add_random_updated_region) { | |
| for (int i = random.Rand(10); i >= 0; i--) { | |
| // At least a 1 x 1 updated region. | |
| const int left = random.Rand(0, frame->size().width() - 2); | |
| const int top = random.Rand(0, frame->size().height() - 2); | |
| const int right = random.Rand(left + 1, frame->size().width()); | |
| const int bottom = random.Rand(top + 1, frame->size().height()); | |
| frame->mutable_updated_region()->AddRect( | |
| DesktopRect::MakeLTRB(left, top, right, bottom)); | |
| } | |
| } | |
| } | |
| // Paints pixels in `rect` of `frame` to `color`. | |
| void PaintRect(DesktopFrame* frame, DesktopRect rect, RgbaColor rgba_color) { | |
| static_assert(DesktopFrame::kBytesPerPixel == sizeof(uint32_t), | |
| "kBytesPerPixel should be 4."); | |
| RTC_DCHECK_GE(frame->size().width(), rect.right()); | |
| RTC_DCHECK_GE(frame->size().height(), rect.bottom()); | |
| // TODO(bugs.webrtc.org/436974448): Support other pixel formats. | |
| RTC_CHECK_EQ(FOURCC_ARGB, frame->pixel_format()); | |
| uint32_t color = rgba_color.ToUInt32(); | |
| uint8_t* row = frame->GetFrameDataAtPos(rect.top_left()); | |
| for (int i = 0; i < rect.height(); i++) { | |
| uint32_t* column = reinterpret_cast<uint32_t*>(row); | |
| for (int j = 0; j < rect.width(); j++) { | |
| column[j] = color; | |
| } | |
| row += frame->stride(); | |
| } | |
| } | |
| // Paints pixels in `region` of `frame` to `color`. | |
| void PaintRegion(DesktopFrame* frame, | |
| DesktopRegion* region, | |
| RgbaColor rgba_color) { | |
| region->IntersectWith(DesktopRect::MakeSize(frame->size())); | |
| for (DesktopRegion::Iterator it(*region); !it.IsAtEnd(); it.Advance()) { | |
| PaintRect(frame, it.rect(), rgba_color); | |
| } | |
| } | |
| } // namespace | |
| DesktopFrameGenerator::DesktopFrameGenerator() {} | |
| DesktopFrameGenerator::~DesktopFrameGenerator() {} | |
| DesktopFramePainter::DesktopFramePainter() {} | |
| DesktopFramePainter::~DesktopFramePainter() {} | |
| PainterDesktopFrameGenerator::PainterDesktopFrameGenerator() | |
| : size_(1024, 768), | |
| return_frame_(true), | |
| provide_updated_region_hints_(false), | |
| enlarge_updated_region_(false), | |
| enlarge_range_(20), | |
| add_random_updated_region_(false), | |
| painter_(nullptr) {} | |
| PainterDesktopFrameGenerator::~PainterDesktopFrameGenerator() {} | |
| std::unique_ptr<DesktopFrame> PainterDesktopFrameGenerator::GetNextFrame( | |
| SharedMemoryFactory* factory) { | |
| if (!return_frame_) { | |
| return nullptr; | |
| } | |
| std::unique_ptr<DesktopFrame> frame = std::unique_ptr<DesktopFrame>( | |
| factory ? SharedMemoryDesktopFrame::Create(size_, FOURCC_ARGB, factory) | |
| .release() | |
| : new BasicDesktopFrame(size_, FOURCC_ARGB)); | |
| if (painter_) { | |
| DesktopRegion updated_region; | |
| if (!painter_->Paint(frame.get(), &updated_region)) { | |
| return nullptr; | |
| } | |
| if (provide_updated_region_hints_) { | |
| SetUpdatedRegion(frame.get(), updated_region, enlarge_updated_region_, | |
| enlarge_range_, add_random_updated_region_); | |
| } else { | |
| frame->mutable_updated_region()->SetRect( | |
| DesktopRect::MakeSize(frame->size())); | |
| } | |
| } | |
| return frame; | |
| } | |
| DesktopSize* PainterDesktopFrameGenerator::size() { | |
| return &size_; | |
| } | |
| void PainterDesktopFrameGenerator::set_return_frame(bool return_frame) { | |
| return_frame_ = return_frame; | |
| } | |
| void PainterDesktopFrameGenerator::set_provide_updated_region_hints( | |
| bool provide_updated_region_hints) { | |
| provide_updated_region_hints_ = provide_updated_region_hints; | |
| } | |
| void PainterDesktopFrameGenerator::set_enlarge_updated_region( | |
| bool enlarge_updated_region) { | |
| enlarge_updated_region_ = enlarge_updated_region; | |
| } | |
| void PainterDesktopFrameGenerator::set_enlarge_range(int enlarge_range) { | |
| enlarge_range_ = enlarge_range; | |
| } | |
| void PainterDesktopFrameGenerator::set_add_random_updated_region( | |
| bool add_random_updated_region) { | |
| add_random_updated_region_ = add_random_updated_region; | |
| } | |
| void PainterDesktopFrameGenerator::set_desktop_frame_painter( | |
| DesktopFramePainter* painter) { | |
| painter_ = painter; | |
| } | |
| BlackWhiteDesktopFramePainter::BlackWhiteDesktopFramePainter() {} | |
| BlackWhiteDesktopFramePainter::~BlackWhiteDesktopFramePainter() {} | |
| DesktopRegion* BlackWhiteDesktopFramePainter::updated_region() { | |
| return &updated_region_; | |
| } | |
| bool BlackWhiteDesktopFramePainter::Paint(DesktopFrame* frame, | |
| DesktopRegion* updated_region) { | |
| RTC_DCHECK(updated_region->is_empty()); | |
| memset(frame->data(), 0, frame->stride() * frame->size().height()); | |
| PaintRegion(frame, &updated_region_, RgbaColor(0xFFFFFFFF)); | |
| updated_region_.Swap(updated_region); | |
| return true; | |
| } | |
| } // namespace webrtc |