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

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

Powered by Google App Engine
This is Rietveld 408576698