// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/client/dual_buffer_frame_consumer.h" #include #include "base/bind.h" #include "base/callback_helpers.h" #include "base/check.h" #include "base/location.h" #include "base/threading/thread_task_runner_handle.h" namespace remoting { namespace { // The implementation is mostly the same as webrtc::BasicDesktopFrame, except // that it has an extra padding of one row at the end of the buffer. This is // to workaround a bug in iOS' implementation of glTexSubimage2D that causes // occasional SIGSEGV. // // glTexSubimage2D is supposed to only read // kBytesPerPixel * width * (height - 1) bytes from the buffer but it seems to // be reading more than that, which may end up reading protected memory. // // See details in crbug.com/778550 class PaddedDesktopFrame : public webrtc::DesktopFrame { public: explicit PaddedDesktopFrame(webrtc::DesktopSize size); PaddedDesktopFrame(const PaddedDesktopFrame&) = delete; PaddedDesktopFrame& operator=(const PaddedDesktopFrame&) = delete; ~PaddedDesktopFrame() override; // Creates a PaddedDesktopFrame that contains copy of |frame|. static std::unique_ptr CopyOf( const webrtc::DesktopFrame& frame); }; PaddedDesktopFrame::PaddedDesktopFrame(webrtc::DesktopSize size) : DesktopFrame( size, kBytesPerPixel * size.width(), new uint8_t[kBytesPerPixel * size.width() * (size.height() + 1)], nullptr) {} PaddedDesktopFrame::~PaddedDesktopFrame() { delete[] data_; } // static std::unique_ptr PaddedDesktopFrame::CopyOf( const webrtc::DesktopFrame& frame) { std::unique_ptr result = std::make_unique(frame.size()); for (int y = 0; y < frame.size().height(); ++y) { memcpy(result->data() + y * result->stride(), frame.data() + y * frame.stride(), frame.size().width() * kBytesPerPixel); } result->CopyFrameInfoFrom(frame); return result; } } // namespace DualBufferFrameConsumer::DualBufferFrameConsumer( RenderCallback callback, scoped_refptr task_runner, protocol::FrameConsumer::PixelFormat format) : callback_(std::move(callback)), task_runner_(task_runner), pixel_format_(format) { weak_ptr_ = weak_factory_.GetWeakPtr(); thread_checker_.DetachFromThread(); } DualBufferFrameConsumer::~DualBufferFrameConsumer() { DCHECK(thread_checker_.CalledOnValidThread()); } void DualBufferFrameConsumer::RequestFullDesktopFrame() { DCHECK(thread_checker_.CalledOnValidThread()); if (!buffers_[0]) { return; } DCHECK(buffers_[0]->size().equals(buffers_[1]->size())); // This creates a copy of buffers_[0] and merges area defined in // |buffer_1_mask_| from buffers_[1] into the copy. std::unique_ptr full_frame = PaddedDesktopFrame::CopyOf(*buffers_[0]); webrtc::DesktopRect desktop_rect = webrtc::DesktopRect::MakeSize(buffers_[0]->size()); for (webrtc::DesktopRegion::Iterator i(buffer_1_mask_); !i.IsAtEnd(); i.Advance()) { full_frame->CopyPixelsFrom(*buffers_[1], i.rect().top_left(), i.rect()); } full_frame->mutable_updated_region()->SetRect(desktop_rect); RunRenderCallback(std::move(full_frame), base::DoNothing()); } std::unique_ptr DualBufferFrameConsumer::AllocateFrame( const webrtc::DesktopSize& size) { DCHECK(thread_checker_.CalledOnValidThread()); // Both buffers are reallocated whenever screen size changes. if (!buffers_[0] || !buffers_[0]->size().equals(size)) { buffers_[0] = webrtc::SharedDesktopFrame::Wrap( std::make_unique(size)); buffers_[1] = webrtc::SharedDesktopFrame::Wrap( std::make_unique(size)); buffer_1_mask_.Clear(); current_buffer_ = 0; } else { current_buffer_ = (current_buffer_ + 1) % 2; } return buffers_[current_buffer_]->Share(); } void DualBufferFrameConsumer::DrawFrame( std::unique_ptr frame, base::OnceClosure done) { DCHECK(thread_checker_.CalledOnValidThread()); webrtc::SharedDesktopFrame* shared_frame = reinterpret_cast (frame.get()); if (shared_frame->GetUnderlyingFrame() == buffers_[1]->GetUnderlyingFrame()) { buffer_1_mask_.AddRegion(frame->updated_region()); } else if (shared_frame->GetUnderlyingFrame() == buffers_[0]->GetUnderlyingFrame()) { buffer_1_mask_.Subtract(frame->updated_region()); } RunRenderCallback(std::move(frame), std::move(done)); } protocol::FrameConsumer::PixelFormat DualBufferFrameConsumer::GetPixelFormat() { return pixel_format_; } base::WeakPtr DualBufferFrameConsumer::GetWeakPtr() { return weak_ptr_; } void DualBufferFrameConsumer::RunRenderCallback( std::unique_ptr frame, base::OnceClosure done) { if (!task_runner_) { callback_.Run(std::move(frame), std::move(done)); return; } task_runner_->PostTask( FROM_HERE, base::BindOnce( callback_, std::move(frame), base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask), base::ThreadTaskRunnerHandle::Get(), FROM_HERE, std::move(done)))); } } // namespace remoting