1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/client/plugin/pepper_video_renderer_2d.h"
8 #include "base/callback_helpers.h"
9 #include "base/strings/string_util.h"
10 #include "base/task_runner_util.h"
11 #include "ppapi/cpp/completion_callback.h"
12 #include "ppapi/cpp/image_data.h"
13 #include "ppapi/cpp/instance.h"
14 #include "ppapi/cpp/point.h"
15 #include "ppapi/cpp/rect.h"
16 #include "ppapi/cpp/size.h"
17 #include "remoting/base/util.h"
18 #include "remoting/client/client_context.h"
19 #include "remoting/client/software_video_renderer.h"
20 #include "remoting/proto/video.pb.h"
21 #include "remoting/protocol/performance_tracker.h"
22 #include "third_party/libyuv/include/libyuv/scale_argb.h"
23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
29 // DesktopFrame that wraps a supplied pp::ImageData
30 class PepperDesktopFrame
: public webrtc::DesktopFrame
{
32 // Wraps the supplied ImageData.
33 explicit PepperDesktopFrame(const pp::ImageData
& buffer
)
35 webrtc::DesktopSize(buffer
.size().width(), buffer
.size().height()),
37 reinterpret_cast<uint8_t*>(buffer
.data()),
41 // Access to underlying pepper representation.
42 const pp::ImageData
& buffer() const {
47 pp::ImageData buffer_
;
52 PepperVideoRenderer2D::PepperVideoRenderer2D()
53 : callback_factory_(this),
54 weak_factory_(this) {}
56 PepperVideoRenderer2D::~PepperVideoRenderer2D() {}
58 bool PepperVideoRenderer2D::Initialize(
59 pp::Instance
* instance
,
60 const ClientContext
& context
,
61 EventHandler
* event_handler
,
62 protocol::PerformanceTracker
* perf_tracker
) {
63 DCHECK(thread_checker_
.CalledOnValidThread());
65 DCHECK(!event_handler_
);
67 DCHECK(event_handler
);
70 event_handler_
= event_handler
;
71 software_video_renderer_
.reset(new SoftwareVideoRenderer(
72 context
.decode_task_runner(), this, perf_tracker
));
77 void PepperVideoRenderer2D::OnViewChanged(const pp::View
& view
) {
78 DCHECK(thread_checker_
.CalledOnValidThread());
80 pp::Rect pp_size
= view
.GetRect();
81 view_size_
= webrtc::DesktopSize(pp_size
.width(), pp_size
.height());
83 // Update scale if graphics2d has been initialized.
84 if (!graphics2d_
.is_null() && source_size_
.width() > 0) {
85 graphics2d_
.SetScale(static_cast<float>(view_size_
.width()) /
86 source_size_
.width());
88 // Bind graphics2d_ again after changing the scale to work around
90 instance_
->BindGraphics(graphics2d_
);
91 bool result
= instance_
->BindGraphics(graphics2d_
);
92 DCHECK(result
) << "Couldn't bind the device context.";
96 void PepperVideoRenderer2D::EnableDebugDirtyRegion(bool enable
) {
97 debug_dirty_region_
= enable
;
100 void PepperVideoRenderer2D::OnSessionConfig(
101 const protocol::SessionConfig
& config
) {
102 DCHECK(thread_checker_
.CalledOnValidThread());
104 software_video_renderer_
->OnSessionConfig(config
);
107 protocol::VideoStub
* PepperVideoRenderer2D::GetVideoStub() {
108 DCHECK(thread_checker_
.CalledOnValidThread());
110 return software_video_renderer_
->GetVideoStub();
113 scoped_ptr
<webrtc::DesktopFrame
> PepperVideoRenderer2D::AllocateFrame(
114 const webrtc::DesktopSize
& size
) {
115 DCHECK(thread_checker_
.CalledOnValidThread());
117 pp::ImageData
buffer_data(instance_
, PP_IMAGEDATAFORMAT_BGRA_PREMUL
,
118 pp::Size(size
.width(), size
.height()), false);
119 return make_scoped_ptr(new PepperDesktopFrame(buffer_data
));
122 void PepperVideoRenderer2D::DrawFrame(scoped_ptr
<webrtc::DesktopFrame
> frame
,
123 const base::Closure
& done
) {
124 DCHECK(thread_checker_
.CalledOnValidThread());
126 if (!frame_received_
) {
127 event_handler_
->OnVideoFirstFrameReceived();
128 frame_received_
= true;
131 bool size_changed
= !source_size_
.equals(frame
->size());
133 source_size_
= frame
->size();
135 // Create a 2D rendering context with the new dimensions.
136 graphics2d_
= pp::Graphics2D(
137 instance_
, pp::Size(source_size_
.width(), source_size_
.height()), true);
138 graphics2d_
.SetScale(static_cast<float>(view_size_
.width()) /
139 source_size_
.width());
140 bool result
= instance_
->BindGraphics(graphics2d_
);
141 DCHECK(result
) << "Couldn't bind the device context.";
145 if (size_changed
|| !source_dpi_
.equals(frame
->dpi())) {
146 source_dpi_
= frame
->dpi();
148 // Notify JavaScript of the change in source size.
149 event_handler_
->OnVideoSize(source_size_
, source_dpi_
);
152 const webrtc::DesktopRegion
* shape
= frame
->shape();
154 if (!source_shape_
|| !source_shape_
->Equals(*shape
)) {
155 source_shape_
= make_scoped_ptr(new webrtc::DesktopRegion(*shape
));
156 event_handler_
->OnVideoShape(source_shape_
.get());
158 } else if (source_shape_
) {
159 source_shape_
= nullptr;
160 event_handler_
->OnVideoShape(nullptr);
163 // If Debug dirty region is enabled then emit it.
164 if (debug_dirty_region_
)
165 event_handler_
->OnVideoFrameDirtyRegion(frame
->updated_region());
167 const pp::ImageData
& image_data
=
168 static_cast<PepperDesktopFrame
*>(frame
.get())->buffer();
169 for (webrtc::DesktopRegion::Iterator
i(frame
->updated_region()); !i
.IsAtEnd();
171 graphics2d_
.PaintImageData(image_data
, pp::Point(0, 0),
172 pp::Rect(i
.rect().left(), i
.rect().top(),
173 i
.rect().width(), i
.rect().height()));
176 if (!done
.is_null()) {
177 pending_frames_done_callbacks_
.push_back(
178 new base::ScopedClosureRunner(done
));
186 FrameConsumer::PixelFormat
PepperVideoRenderer2D::GetPixelFormat() {
190 void PepperVideoRenderer2D::Flush() {
191 DCHECK(thread_checker_
.CalledOnValidThread());
193 if (flush_pending_
|| !need_flush_
)
198 // Move callbacks from |pending_frames_done_callbacks_| to
199 // |flushing_frames_done_callbacks_| so the callbacks are called when flush is
201 DCHECK(flushing_frames_done_callbacks_
.empty());
202 flushing_frames_done_callbacks_
= pending_frames_done_callbacks_
.Pass();
204 // Flush the updated areas to the screen.
205 int error
= graphics2d_
.Flush(
206 callback_factory_
.NewCallback(&PepperVideoRenderer2D::OnFlushDone
));
207 CHECK(error
== PP_OK_COMPLETIONPENDING
);
208 flush_pending_
= true;
211 void PepperVideoRenderer2D::OnFlushDone(int result
) {
212 DCHECK(thread_checker_
.CalledOnValidThread());
214 DCHECK(flush_pending_
);
215 flush_pending_
= false;
217 // Call all callbacks for the frames we've just flushed.
218 flushing_frames_done_callbacks_
.clear();
220 // Flush again if necessary.
224 } // namespace remoting