Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(584)

Side by Side Diff: remoting/client/plugin/pepper_video_renderer_3d.cc

Issue 820823002: Implement video renderer based on VideoDecode API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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.
4
5 #include "remoting/client/plugin/pepper_video_renderer_3d.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/stl_util.h"
9 #include "ppapi/c/pp_codecs.h"
10 #include "ppapi/c/ppb_opengles2.h"
11 #include "ppapi/cpp/instance.h"
12 #include "ppapi/lib/gl/include/GLES2/gl2ext.h"
13 #include "remoting/proto/video.pb.h"
14 #include "remoting/protocol/session_config.h"
15
16 namespace remoting {
17
18 class PepperVideoRenderer3D::PendingPacket {
19 public:
20 PendingPacket(scoped_ptr<VideoPacket> packet, const base::Closure& done)
21 : packet_(packet.Pass()),
22 done_runner_(done) {
23 }
24
25 ~PendingPacket() {}
26
27 const VideoPacket* packet() const { return packet_.get(); }
28
29 private:
30 scoped_ptr<VideoPacket> packet_;
31 base::ScopedClosureRunner done_runner_;
32 };
33
34 struct PepperVideoRenderer3D::FrameDecodeTimer {
35 FrameDecodeTimer(uint32_t frame_id, base::TimeTicks decode_started_time)
36 : frame_id(frame_id), decode_started_time(decode_started_time){};
37 uint32_t frame_id;
38 base::TimeTicks decode_started_time;
39 };
40
41 PepperVideoRenderer3D::PepperVideoRenderer3D()
42 : event_handler_(nullptr),
43 view_width_(0),
44 view_height_(0),
45 latest_sequence_number_(0),
46 initialized_(false),
47 decode_pending_(false),
48 get_picture_pending_(false),
49 paint_pending_(false),
50 last_frame_id_(0),
51 need_repaint_(false),
52 current_shader_program_texture_target_(0),
53 shader_program_(0),
54 shader_texcoord_scale_location_(0),
55 callback_factory_(this) {
56 }
57
58 PepperVideoRenderer3D::~PepperVideoRenderer3D() {
59 RecyclePictures(false);
60
61 if (shader_program_)
62 gles2_if_->DeleteProgram(graphics_.pp_resource(), shader_program_);
63
64 STLDeleteElements(&pending_packets_);
65 }
66
67 bool PepperVideoRenderer3D::Initialize(pp::Instance* instance,
68 const ClientContext& context,
69 EventHandler* event_handler) {
70 DCHECK(event_handler);
71 DCHECK(!event_handler_);
72
73 event_handler_ = event_handler;
74
75 const int32_t context_attributes[] = {
76 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
77 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
78 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
79 PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
80 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
81 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
82 PP_GRAPHICS3DATTRIB_SAMPLES, 0,
83 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
84 PP_GRAPHICS3DATTRIB_WIDTH, 1,
85 PP_GRAPHICS3DATTRIB_HEIGHT, 1,
86 PP_GRAPHICS3DATTRIB_NONE,
87 };
88 graphics_ = pp::Graphics3D(instance, context_attributes);
89
90 if (graphics_.is_null()) {
91 LOG(WARNING) << "Graphics3D interface is not available.";
92 return false;
93 }
94 if (!instance->BindGraphics(graphics_)) {
95 LOG(WARNING) << "Failed to bind Graphics3D.";
96 return false;
97 }
98
99 // Fetch the GLES2 interface to use to render frames.
100 gles2_if_ = static_cast<const PPB_OpenGLES2*>(
101 pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
102 CHECK(gles2_if_);
103
104 video_decoder_ = pp::VideoDecoder(instance);
105 if (video_decoder_.is_null()) {
106 LOG(WARNING) << "VideoDecoder interface is not available.";
107 return false;
108 }
109
110 PP_Resource graphics_3d = graphics_.pp_resource();
111
112 gles2_if_->ClearColor(graphics_3d, 1, 0, 0, 1);
113 gles2_if_->Clear(graphics_3d, GL_COLOR_BUFFER_BIT);
114
115 // Assign vertex positions and texture coordinates to buffers for use in
116 // shader program.
117 static const float kVertices[] = {
118 -1, -1, -1, 1, 1, -1, 1, 1, // Position coordinates.
119 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
120 };
121
122 GLuint buffer;
123 gles2_if_->GenBuffers(graphics_3d, 1, &buffer);
124 gles2_if_->BindBuffer(graphics_3d, GL_ARRAY_BUFFER, buffer);
125 gles2_if_->BufferData(graphics_3d, GL_ARRAY_BUFFER, sizeof(kVertices),
126 kVertices, GL_STATIC_DRAW);
127
128 CheckGLError();
129
130 return true;
131 }
132
133 void PepperVideoRenderer3D::OnViewChanged(const pp::View& view) {
134 pp::Size size = view.GetRect().size();
135 float scale = view.GetDeviceScale();
136 view_width_ = ceilf(size.width() * scale);
137 view_height_ = ceilf(size.height() * scale);
138 graphics_.ResizeBuffers(view_width_, view_height_);
139
140 need_repaint_ = true;
141 DoPaint();
142 }
143
144 void PepperVideoRenderer3D::OnSessionConfig(
145 const protocol::SessionConfig& config) {
146 PP_VideoProfile video_profile = PP_VIDEOPROFILE_VP8_ANY;
147 switch (config.video_config().codec) {
148 case protocol::ChannelConfig::CODEC_VP8:
149 video_profile = PP_VIDEOPROFILE_VP8_ANY;
150 break;
151 case protocol::ChannelConfig::CODEC_VP9:
152 video_profile = PP_VIDEOPROFILE_VP9_ANY;
153 break;
154 default:
155 NOTREACHED();
156 }
157 int32_t result = video_decoder_.Initialize(
158 graphics_, video_profile, PP_HARDWAREACCELERATION_WITHFALLBACK,
159 callback_factory_.NewCallback(&PepperVideoRenderer3D::OnInitialized));
160 CHECK_EQ(result, PP_OK_COMPLETIONPENDING)
161 << "video_decoder_.Initialize() returned " << result;
162 }
163
164 ChromotingStats* PepperVideoRenderer3D::GetStats() {
165 return &stats_;
166 }
167
168 void PepperVideoRenderer3D::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
169 const base::Closure& done) {
170 base::ScopedClosureRunner done_runner(done);
171
172 // Don't need to do anything if the packet is empty. Host sends empty video
173 // packets when the screen is not changing.
174 if (!packet->data().size())
175 return;
176
177 // Update statistics.
178 stats_.video_frame_rate()->Record(1);
179 stats_.video_bandwidth()->Record(packet->data().size());
180 if (packet->has_capture_time_ms())
181 stats_.video_capture_ms()->Record(packet->capture_time_ms());
182 if (packet->has_encode_time_ms())
183 stats_.video_encode_ms()->Record(packet->encode_time_ms());
184 if (packet->has_client_sequence_number() &&
185 packet->client_sequence_number() > latest_sequence_number_) {
186 latest_sequence_number_ = packet->client_sequence_number();
187 base::TimeDelta round_trip_latency =
188 base::Time::Now() -
189 base::Time::FromInternalValue(packet->client_sequence_number());
190 stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
191 }
192
193 webrtc::DesktopSize frame_size(packet->format().screen_width(),
194 packet->format().screen_height());
195 webrtc::DesktopVector frame_dpi(packet->format().x_dpi(),
196 packet->format().y_dpi());
197 if ((!frame_size.is_empty() && !frame_size_.equals(frame_size)) ||
198 !frame_dpi_.equals(frame_dpi)) {
199 frame_dpi_ = frame_dpi;
200 event_handler_->OnVideoSize(frame_size, frame_dpi);
201 }
202
203 // Update the desktop shape region.
204 webrtc::DesktopRegion desktop_shape;
205 if (packet->has_use_desktop_shape()) {
206 for (int i = 0; i < packet->desktop_shape_rects_size(); ++i) {
207 Rect remoting_rect = packet->desktop_shape_rects(i);
208 desktop_shape.AddRect(webrtc::DesktopRect::MakeXYWH(
209 remoting_rect.x(), remoting_rect.y(),
210 remoting_rect.width(), remoting_rect.height()));
211 }
212 } else {
213 // Fallback for the case when the host didn't include the desktop shape.
214 desktop_shape =
215 webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(frame_size));
216 }
217
218 if (!desktop_shape_.Equals(desktop_shape)) {
219 desktop_shape_.Swap(&desktop_shape);
220 event_handler_->OnVideoShape(desktop_shape_);
221 }
222
223 pending_packets_.push_back(
224 new PendingPacket(packet.Pass(), done_runner.Release()));
225 DoDecode();
226 }
227
228 void PepperVideoRenderer3D::OnInitialized(int32_t result) {
229 CHECK_EQ(result, PP_OK) << "VideoDecoder::Initialize() failed";
230 initialized_ = true;
231 DoDecode();
232 }
233
234 void PepperVideoRenderer3D::DoGetPicture() {
235 if (get_picture_pending_)
236 return;
237
238 int32_t result =
239 video_decoder_.GetPicture(callback_factory_.NewCallbackWithOutput(
240 &PepperVideoRenderer3D::OnPictureReady));
241 CHECK_EQ(result, PP_OK_COMPLETIONPENDING);
242 get_picture_pending_ = true;
243 }
244
245 void PepperVideoRenderer3D::DoDecode() {
246 if (!initialized_ || decode_pending_ || pending_packets_.empty())
247 return;
248
249 ++last_frame_id_;
250 frames_decode_timers_.push_back(
251 FrameDecodeTimer(last_frame_id_, base::TimeTicks::Now()));
252
253 const VideoPacket* packet = pending_packets_.front()->packet();
254
255 int32_t result = video_decoder_.Decode(
256 last_frame_id_, packet->data().size(), packet->data().data(),
257 callback_factory_.NewCallback(&PepperVideoRenderer3D::OnDecodeDone));
258 CHECK_EQ(result, PP_OK_COMPLETIONPENDING);
259 decode_pending_ = true;
260 }
261
262 void PepperVideoRenderer3D::OnDecodeDone(int32_t result) {
263 decode_pending_ = false;
264
265 if (result != PP_OK) {
266 LOG(ERROR) << "VideoDecoder::Decode() returned " << result;
267 event_handler_->OnVideoDecoderFailed();
268 return;
269 }
270
271 delete pending_packets_.front();
272 pending_packets_.pop_front();
273
274 DoDecode();
275 DoGetPicture();
276 }
277
278 void PepperVideoRenderer3D::OnPictureReady(int32_t result,
279 PP_VideoPicture picture) {
280 get_picture_pending_ = false;
281
282 if (result == PP_ERROR_ABORTED)
283 return;
284
285 if (result != PP_OK) {
286 LOG(ERROR) << "VideoDecoder::GetPicture() returned " << result;
287 event_handler_->OnVideoDecoderFailed();
288 return;
289 }
290
291 CHECK(!frames_decode_timers_.empty());
292 const FrameDecodeTimer& frame_timer = frames_decode_timers_.front();
293 DCHECK_EQ(picture.decode_id, frame_timer.frame_id);
294 stats_.video_decode_ms()->Record(
295 (base::TimeTicks::Now() - frame_timer.decode_started_time)
296 .InMilliseconds());
297 frames_decode_timers_.pop_front();
298
299 // Recycle all pending pictures, but keep the first one if it's still being
300 // painted.
301 RecyclePictures(paint_pending_);
302 pending_pictures_.push_back(picture);
303 need_repaint_ = true;
304
305 DoPaint();
306 DoGetPicture();
307 }
308
309 void PepperVideoRenderer3D::RecyclePictures(bool keep_first) {
310 std::vector<PP_VideoPicture>::iterator it = pending_pictures_.begin();
311 if (keep_first)
312 ++it;
313
314 while (it != pending_pictures_.end()) {
315 video_decoder_.RecyclePicture(*it);
316 it = pending_pictures_.erase(it);
317 }
318 }
319
320 void PepperVideoRenderer3D::DoPaint() {
321 if (paint_pending_ || !need_repaint_ || pending_pictures_.empty())
322 return;
323
324 need_repaint_ = false;
325 last_paint_started_time_ = base::TimeTicks::Now();
326
327 const PP_VideoPicture& picture = pending_pictures_.front();
328 PP_Resource graphics_3d = graphics_.pp_resource();
329
330 EnsureProgramForTexture(picture.texture_target);
331
332 gles2_if_->UseProgram(graphics_3d, shader_program_);
333 if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) {
334 gles2_if_->Uniform2f(graphics_3d, shader_texcoord_scale_location_,
335 static_cast<GLfloat>(picture.texture_size.width),
336 static_cast<GLfloat>(picture.texture_size.height));
337 } else {
338 gles2_if_->Uniform2f(
339 graphics_3d, shader_texcoord_scale_location_, 1.0, 1.0);
340 }
341
342 // Set viewport position & dimensions.
343 gles2_if_->Viewport(graphics_3d, 0, 0, view_width_, view_height_);
344
345 // Select the texture unit GL_TEXTURE0.
346 gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
347
348 // Select the texture.
349 gles2_if_->BindTexture(graphics_3d, picture.texture_target,
350 picture.texture_id);
351
352 // Select linear filter in case the texture needs to be scaled.
353 gles2_if_->TexParameteri(graphics_3d, picture.texture_target,
354 GL_TEXTURE_MIN_FILTER, GL_LINEAR);
355
356 // Render texture by drawing a triangle strip with 4 vertices.
357 gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
358
359 CheckGLError();
360
361 // Request PPAPI display the queue texture.
362 int32_t result = graphics_.SwapBuffers(
363 callback_factory_.NewCallback(&PepperVideoRenderer3D::OnPaintDone));
364 CHECK_EQ(result, PP_OK_COMPLETIONPENDING);
365 paint_pending_ = true;
366 }
367
368 void PepperVideoRenderer3D::OnPaintDone(int32_t result) {
369 CHECK_EQ(result, PP_OK) << "Graphics3D::SwapBuffers() failed";
370
371 paint_pending_ = false;
372 stats_.video_paint_ms()->Record(
373 (base::TimeTicks::Now() - last_paint_started_time_).InMilliseconds());
374
375 if (pending_pictures_.size() > 1) {
376 video_decoder_.RecyclePicture(pending_pictures_.front());
377 pending_pictures_.erase(pending_pictures_.begin());
378 need_repaint_ = true;
379 }
380
381 DoPaint();
382 }
383
384 void PepperVideoRenderer3D::EnsureProgramForTexture(uint32_t texture_target) {
385 static const char kVertexShader[] =
386 "varying vec2 v_texCoord; \n"
387 "attribute vec4 a_position; \n"
388 "attribute vec2 a_texCoord; \n"
389 "uniform vec2 v_scale; \n"
390 "void main() \n"
391 "{ \n"
392 " v_texCoord = v_scale * a_texCoord; \n"
393 " gl_Position = a_position; \n"
394 "}";
395
396 static const char kFragmentShader2D[] =
397 "precision mediump float; \n"
398 "varying vec2 v_texCoord; \n"
399 "uniform sampler2D s_texture; \n"
400 "void main() \n"
401 "{"
402 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
403 "}";
404
405 static const char kFragmentShaderRectangle[] =
406 "#extension GL_ARB_texture_rectangle : require\n"
407 "precision mediump float; \n"
408 "varying vec2 v_texCoord; \n"
409 "uniform sampler2DRect s_texture; \n"
410 "void main() \n"
411 "{"
412 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
413 "}";
414
415 static const char kFragmentShaderExternal[] =
416 "#extension GL_OES_EGL_image_external : require\n"
417 "precision mediump float; \n"
418 "varying vec2 v_texCoord; \n"
419 "uniform samplerExternalOES s_texture; \n"
420 "void main() \n"
421 "{"
422 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
423 "}";
424
425 // Initialize shader program only if texture type has changed.
426 if (current_shader_program_texture_target_ != texture_target) {
427 current_shader_program_texture_target_ = texture_target;
428
429 if (texture_target == GL_TEXTURE_2D) {
430 CreateProgram(kVertexShader, kFragmentShader2D);
431 } else if (texture_target == GL_TEXTURE_RECTANGLE_ARB) {
432 CreateProgram(kVertexShader, kFragmentShaderRectangle);
433 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) {
434 CreateProgram(kVertexShader, kFragmentShaderExternal);
435 } else {
436 CHECK(false);
437 }
438 }
439 }
440
441 void PepperVideoRenderer3D::CreateProgram(const char* vertex_shader,
442 const char* fragment_shader) {
443 PP_Resource graphics_3d = graphics_.pp_resource();
444 if (shader_program_)
445 gles2_if_->DeleteProgram(graphics_3d, shader_program_);
446
447 // Create shader program.
448 shader_program_ = gles2_if_->CreateProgram(graphics_3d);
449 CreateShaderProgram(GL_VERTEX_SHADER, vertex_shader);
450 CreateShaderProgram(GL_FRAGMENT_SHADER, fragment_shader);
451 gles2_if_->LinkProgram(graphics_3d, shader_program_);
452 gles2_if_->UseProgram(graphics_3d, shader_program_);
453 gles2_if_->Uniform1i(
454 graphics_3d,
455 gles2_if_->GetUniformLocation(graphics_3d, shader_program_, "s_texture"),
456 0);
457 CheckGLError();
458
459 shader_texcoord_scale_location_ = gles2_if_->GetUniformLocation(
460 graphics_3d, shader_program_, "v_scale");
461
462 GLint pos_location = gles2_if_->GetAttribLocation(
463 graphics_3d, shader_program_, "a_position");
464 GLint tc_location = gles2_if_->GetAttribLocation(
465 graphics_3d, shader_program_, "a_texCoord");
466 CheckGLError();
467
468 // Construct the vertex array for DrawArrays(), using the buffer created in
469 // Initialize().
470 gles2_if_->EnableVertexAttribArray(graphics_3d, pos_location);
471 gles2_if_->VertexAttribPointer(graphics_3d, pos_location, 2, GL_FLOAT,
472 GL_FALSE, 0, 0);
473 gles2_if_->EnableVertexAttribArray(graphics_3d, tc_location);
474 gles2_if_->VertexAttribPointer(
475 graphics_3d, tc_location, 2, GL_FLOAT, GL_FALSE, 0,
476 static_cast<float*>(0) + 8); // Skip position coordinates.
477
478 gles2_if_->UseProgram(graphics_3d, 0);
479
480 CheckGLError();
481 }
482
483 void PepperVideoRenderer3D::CreateShaderProgram(int type, const char* source) {
484 int size = strlen(source);
485 GLuint shader = gles2_if_->CreateShader(graphics_.pp_resource(), type);
486 gles2_if_->ShaderSource(graphics_.pp_resource(), shader, 1, &source, &size);
487 gles2_if_->CompileShader(graphics_.pp_resource(), shader);
488 gles2_if_->AttachShader(graphics_.pp_resource(), shader_program_, shader);
489 gles2_if_->DeleteShader(graphics_.pp_resource(), shader);
490 }
491 void PepperVideoRenderer3D::CheckGLError() {
492 GLenum error = gles2_if_->GetError(graphics_.pp_resource());
493 CHECK_EQ(error, static_cast<GLenum>(GL_NO_ERROR)) << "GL error: " << error;
494 }
495
496 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698