blob: ab6c5b62071efa4c39207157fae77db816f0c2d4 [file] [log] [blame]
Avi Drissmand6cdf9b2022-09-15 19:52:531// Copyright 2016 The Chromium Authors
yuweih6d9bb4a2016-07-22 19:45:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/client/dual_buffer_frame_consumer.h"
6
Yuwei Huang9b869462017-10-30 20:36:207#include <memory>
8
Hans Wennborg828a0972020-04-27 18:10:099#include "base/check.h"
Avi Drissman135261e2023-01-11 22:43:1510#include "base/functional/bind.h"
11#include "base/functional/callback_helpers.h"
yuweih6d9bb4a2016-07-22 19:45:0812#include "base/location.h"
Sean Maher5b9af51f2022-11-21 15:32:4713#include "base/task/single_thread_task_runner.h"
yuweih6d9bb4a2016-07-22 19:45:0814
15namespace remoting {
16
Yuwei Huang9b869462017-10-30 20:36:2017namespace {
18
19// The implementation is mostly the same as webrtc::BasicDesktopFrame, except
20// that it has an extra padding of one row at the end of the buffer. This is
21// to workaround a bug in iOS' implementation of glTexSubimage2D that causes
22// occasional SIGSEGV.
23//
24// glTexSubimage2D is supposed to only read
25// kBytesPerPixel * width * (height - 1) bytes from the buffer but it seems to
26// be reading more than that, which may end up reading protected memory.
27//
28// See details in crbug.com/778550
29class PaddedDesktopFrame : public webrtc::DesktopFrame {
30 public:
31 explicit PaddedDesktopFrame(webrtc::DesktopSize size);
Peter Boströme9178e42021-09-22 18:11:4932
33 PaddedDesktopFrame(const PaddedDesktopFrame&) = delete;
34 PaddedDesktopFrame& operator=(const PaddedDesktopFrame&) = delete;
35
Yuwei Huang9b869462017-10-30 20:36:2036 ~PaddedDesktopFrame() override;
37
38 // Creates a PaddedDesktopFrame that contains copy of |frame|.
39 static std::unique_ptr<webrtc::DesktopFrame> CopyOf(
40 const webrtc::DesktopFrame& frame);
Yuwei Huang9b869462017-10-30 20:36:2041};
42
43PaddedDesktopFrame::PaddedDesktopFrame(webrtc::DesktopSize size)
44 : DesktopFrame(
45 size,
46 kBytesPerPixel * size.width(),
47 new uint8_t[kBytesPerPixel * size.width() * (size.height() + 1)],
48 nullptr) {}
49
50PaddedDesktopFrame::~PaddedDesktopFrame() {
51 delete[] data_;
52}
53
54// static
55std::unique_ptr<webrtc::DesktopFrame> PaddedDesktopFrame::CopyOf(
56 const webrtc::DesktopFrame& frame) {
57 std::unique_ptr<PaddedDesktopFrame> result =
58 std::make_unique<PaddedDesktopFrame>(frame.size());
59 for (int y = 0; y < frame.size().height(); ++y) {
60 memcpy(result->data() + y * result->stride(),
61 frame.data() + y * frame.stride(),
62 frame.size().width() * kBytesPerPixel);
63 }
64 result->CopyFrameInfoFrom(frame);
65 return result;
66}
67
68} // namespace
69
yuweih6d9bb4a2016-07-22 19:45:0870DualBufferFrameConsumer::DualBufferFrameConsumer(
Joey Scarr21dd6fdb2020-02-20 00:03:1971 RenderCallback callback,
yuweih6d9bb4a2016-07-22 19:45:0872 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
73 protocol::FrameConsumer::PixelFormat format)
Joey Scarr21dd6fdb2020-02-20 00:03:1974 : callback_(std::move(callback)),
75 task_runner_(task_runner),
76 pixel_format_(format) {
yuweih6d9bb4a2016-07-22 19:45:0877 weak_ptr_ = weak_factory_.GetWeakPtr();
78 thread_checker_.DetachFromThread();
79}
80
81DualBufferFrameConsumer::~DualBufferFrameConsumer() {
82 DCHECK(thread_checker_.CalledOnValidThread());
83}
84
85void DualBufferFrameConsumer::RequestFullDesktopFrame() {
86 DCHECK(thread_checker_.CalledOnValidThread());
87 if (!buffers_[0]) {
88 return;
89 }
90 DCHECK(buffers_[0]->size().equals(buffers_[1]->size()));
91 // This creates a copy of buffers_[0] and merges area defined in
92 // |buffer_1_mask_| from buffers_[1] into the copy.
Yuwei Huang9b869462017-10-30 20:36:2093 std::unique_ptr<webrtc::DesktopFrame> full_frame =
94 PaddedDesktopFrame::CopyOf(*buffers_[0]);
yuweih6d9bb4a2016-07-22 19:45:0895 webrtc::DesktopRect desktop_rect =
96 webrtc::DesktopRect::MakeSize(buffers_[0]->size());
97 for (webrtc::DesktopRegion::Iterator i(buffer_1_mask_); !i.IsAtEnd();
98 i.Advance()) {
99 full_frame->CopyPixelsFrom(*buffers_[1], i.rect().top_left(),
100 i.rect());
101 }
102 full_frame->mutable_updated_region()->SetRect(desktop_rect);
103
Peter Kasting341e1fb2018-02-24 00:03:01104 RunRenderCallback(std::move(full_frame), base::DoNothing());
yuweih6d9bb4a2016-07-22 19:45:08105}
106
107std::unique_ptr<webrtc::DesktopFrame> DualBufferFrameConsumer::AllocateFrame(
108 const webrtc::DesktopSize& size) {
109 DCHECK(thread_checker_.CalledOnValidThread());
110
111 // Both buffers are reallocated whenever screen size changes.
112 if (!buffers_[0] || !buffers_[0]->size().equals(size)) {
113 buffers_[0] = webrtc::SharedDesktopFrame::Wrap(
Jinho Bang138fde32018-01-18 23:13:42114 std::make_unique<PaddedDesktopFrame>(size));
yuweih6d9bb4a2016-07-22 19:45:08115 buffers_[1] = webrtc::SharedDesktopFrame::Wrap(
Jinho Bang138fde32018-01-18 23:13:42116 std::make_unique<PaddedDesktopFrame>(size));
yuweih6d9bb4a2016-07-22 19:45:08117 buffer_1_mask_.Clear();
118 current_buffer_ = 0;
119 } else {
120 current_buffer_ = (current_buffer_ + 1) % 2;
121 }
122 return buffers_[current_buffer_]->Share();
123}
124
125void DualBufferFrameConsumer::DrawFrame(
126 std::unique_ptr<webrtc::DesktopFrame> frame,
Joey Scarr00b7c1f2020-02-13 03:34:03127 base::OnceClosure done) {
yuweih6d9bb4a2016-07-22 19:45:08128 DCHECK(thread_checker_.CalledOnValidThread());
129 webrtc::SharedDesktopFrame* shared_frame =
130 reinterpret_cast<webrtc::SharedDesktopFrame*> (frame.get());
131 if (shared_frame->GetUnderlyingFrame() == buffers_[1]->GetUnderlyingFrame()) {
132 buffer_1_mask_.AddRegion(frame->updated_region());
133 } else if (shared_frame->GetUnderlyingFrame() ==
134 buffers_[0]->GetUnderlyingFrame()) {
135 buffer_1_mask_.Subtract(frame->updated_region());
136 }
Joey Scarr00b7c1f2020-02-13 03:34:03137 RunRenderCallback(std::move(frame), std::move(done));
yuweih6d9bb4a2016-07-22 19:45:08138}
139
140protocol::FrameConsumer::PixelFormat
141DualBufferFrameConsumer::GetPixelFormat() {
142 return pixel_format_;
143}
144
145base::WeakPtr<DualBufferFrameConsumer> DualBufferFrameConsumer::GetWeakPtr() {
146 return weak_ptr_;
147}
148
149void DualBufferFrameConsumer::RunRenderCallback(
150 std::unique_ptr<webrtc::DesktopFrame> frame,
Joey Scarr00b7c1f2020-02-13 03:34:03151 base::OnceClosure done) {
yuweih6d9bb4a2016-07-22 19:45:08152 if (!task_runner_) {
Joey Scarr00b7c1f2020-02-13 03:34:03153 callback_.Run(std::move(frame), std::move(done));
yuweih6d9bb4a2016-07-22 19:45:08154 return;
155 }
156
157 task_runner_->PostTask(
kylechar2caf4d62019-02-15 20:03:42158 FROM_HERE,
159 base::BindOnce(
160 callback_, std::move(frame),
Joey Scarr00b7c1f2020-02-13 03:34:03161 base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
Sean Maher5b9af51f2022-11-21 15:32:47162 base::SingleThreadTaskRunner::GetCurrentDefault(),
163 FROM_HERE, std::move(done))));
yuweih6d9bb4a2016-07-22 19:45:08164}
165
166} // namespace remoting