OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/gpu/media/rendering_helper.h" | 5 #include "content/common/gpu/media/rendering_helper.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <numeric> | 8 #include <numeric> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/callback_helpers.h" | |
12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
13 #include "base/mac/scoped_nsautorelease_pool.h" | 14 #include "base/mac/scoped_nsautorelease_pool.h" |
14 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
16 #include "base/stl_util.h" | |
15 #include "base/strings/stringize_macros.h" | 17 #include "base/strings/stringize_macros.h" |
16 #include "base/synchronization/waitable_event.h" | 18 #include "base/synchronization/waitable_event.h" |
17 #include "ui/gl/gl_context.h" | 19 #include "ui/gl/gl_context.h" |
18 #include "ui/gl/gl_implementation.h" | 20 #include "ui/gl/gl_implementation.h" |
19 #include "ui/gl/gl_surface.h" | 21 #include "ui/gl/gl_surface.h" |
20 #include "ui/gl/gl_surface_egl.h" | 22 #include "ui/gl/gl_surface_egl.h" |
21 #include "ui/gl/gl_surface_glx.h" | 23 #include "ui/gl/gl_surface_glx.h" |
22 | 24 |
23 #if defined(OS_WIN) | 25 #if defined(OS_WIN) |
24 #include <windows.h> | 26 #include <windows.h> |
(...skipping 28 matching lines...) Expand all Loading... | |
53 glDeleteShader(shader); | 55 glDeleteShader(shader); |
54 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 56 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
55 } | 57 } |
56 | 58 |
57 namespace content { | 59 namespace content { |
58 | 60 |
59 RenderingHelperParams::RenderingHelperParams() {} | 61 RenderingHelperParams::RenderingHelperParams() {} |
60 | 62 |
61 RenderingHelperParams::~RenderingHelperParams() {} | 63 RenderingHelperParams::~RenderingHelperParams() {} |
62 | 64 |
65 VideoFrame::VideoFrame(uint32 texture_target, | |
66 uint32 texture_id, | |
67 const base::Closure& no_longer_needed_cb) | |
68 : texture_target_(texture_target), | |
69 texture_id_(texture_id), | |
70 no_longer_needed_cb_(no_longer_needed_cb) { | |
71 DCHECK(!no_longer_needed_cb_.is_null()); | |
72 } | |
73 | |
74 VideoFrame::~VideoFrame() { | |
75 base::ResetAndReturn(&no_longer_needed_cb_).Run(); | |
76 } | |
77 | |
63 // static | 78 // static |
64 bool RenderingHelper::InitializeOneOff() { | 79 bool RenderingHelper::InitializeOneOff() { |
65 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | 80 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
66 #if GL_VARIANT_GLX | 81 #if GL_VARIANT_GLX |
67 cmd_line->AppendSwitchASCII(switches::kUseGL, | 82 cmd_line->AppendSwitchASCII(switches::kUseGL, |
68 gfx::kGLImplementationDesktopName); | 83 gfx::kGLImplementationDesktopName); |
69 #else | 84 #else |
70 cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationEGLName); | 85 cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationEGLName); |
71 #endif | 86 #endif |
72 return gfx::GLSurface::InitializeOneOff(); | 87 return gfx::GLSurface::InitializeOneOff(); |
73 } | 88 } |
74 | 89 |
75 RenderingHelper::RenderingHelper() { | 90 RenderingHelper::RenderingHelper() { |
76 window_ = gfx::kNullAcceleratedWidget; | 91 window_ = gfx::kNullAcceleratedWidget; |
77 Clear(); | 92 Clear(); |
78 } | 93 } |
79 | 94 |
80 RenderingHelper::~RenderingHelper() { | 95 RenderingHelper::~RenderingHelper() { |
81 CHECK_EQ(clients_.size(), 0U) << "Must call UnInitialize before dtor."; | 96 CHECK_EQ(videos_.size(), 0U) << "Must call UnInitialize before dtor."; |
82 Clear(); | 97 Clear(); |
83 } | 98 } |
84 | 99 |
85 void RenderingHelper::Initialize(const RenderingHelperParams& params, | 100 void RenderingHelper::Initialize(const RenderingHelperParams& params, |
86 base::WaitableEvent* done) { | 101 base::WaitableEvent* done) { |
87 // Use cients_.size() != 0 as a proxy for the class having already been | 102 // Use videos_.size() != 0 as a proxy for the class having already been |
88 // Initialize()'d, and UnInitialize() before continuing. | 103 // Initialize()'d, and UnInitialize() before continuing. |
89 if (clients_.size()) { | 104 if (videos_.size()) { |
90 base::WaitableEvent done(false, false); | 105 base::WaitableEvent done(false, false); |
91 UnInitialize(&done); | 106 UnInitialize(&done); |
92 done.Wait(); | 107 done.Wait(); |
93 } | 108 } |
94 | 109 |
95 frame_duration_ = params.rendering_fps > 0 | 110 frame_duration_ = params.rendering_fps > 0 |
96 ? base::TimeDelta::FromSeconds(1) / params.rendering_fps | 111 ? base::TimeDelta::FromSeconds(1) / params.rendering_fps |
97 : base::TimeDelta(); | 112 : base::TimeDelta(); |
98 | 113 |
99 render_as_thumbnails_ = params.render_as_thumbnails; | 114 render_as_thumbnails_ = params.render_as_thumbnails; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
146 #else | 161 #else |
147 #error unknown platform | 162 #error unknown platform |
148 #endif | 163 #endif |
149 CHECK(window_ != gfx::kNullAcceleratedWidget); | 164 CHECK(window_ != gfx::kNullAcceleratedWidget); |
150 | 165 |
151 gl_surface_ = gfx::GLSurface::CreateViewGLSurface(window_); | 166 gl_surface_ = gfx::GLSurface::CreateViewGLSurface(window_); |
152 gl_context_ = gfx::GLContext::CreateGLContext( | 167 gl_context_ = gfx::GLContext::CreateGLContext( |
153 NULL, gl_surface_, gfx::PreferIntegratedGpu); | 168 NULL, gl_surface_, gfx::PreferIntegratedGpu); |
154 gl_context_->MakeCurrent(gl_surface_); | 169 gl_context_->MakeCurrent(gl_surface_); |
155 | 170 |
156 clients_ = params.clients; | 171 CHECK_GT(params.window_sizes.size(), 0U); |
157 CHECK_GT(clients_.size(), 0U); | 172 videos_.resize(params.window_sizes.size()); |
158 LayoutRenderingAreas(); | 173 LayoutRenderingAreas(params.window_sizes); |
159 | 174 |
160 if (render_as_thumbnails_) { | 175 if (render_as_thumbnails_) { |
161 CHECK_EQ(clients_.size(), 1U); | 176 CHECK_EQ(videos_.size(), 1U); |
162 | 177 |
163 GLint max_texture_size; | 178 GLint max_texture_size; |
164 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); | 179 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); |
165 CHECK_GE(max_texture_size, params.thumbnails_page_size.width()); | 180 CHECK_GE(max_texture_size, params.thumbnails_page_size.width()); |
166 CHECK_GE(max_texture_size, params.thumbnails_page_size.height()); | 181 CHECK_GE(max_texture_size, params.thumbnails_page_size.height()); |
167 | 182 |
168 thumbnails_fbo_size_ = params.thumbnails_page_size; | 183 thumbnails_fbo_size_ = params.thumbnails_page_size; |
169 thumbnail_size_ = params.thumbnail_size; | 184 thumbnail_size_ = params.thumbnail_size; |
170 | 185 |
171 glGenFramebuffersEXT(1, &thumbnails_fbo_id_); | 186 glGenFramebuffersEXT(1, &thumbnails_fbo_id_); |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
363 GLSetViewPort(area); | 378 GLSetViewPort(area); |
364 RenderTexture(texture_target, texture_id); | 379 RenderTexture(texture_target, texture_id); |
365 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); | 380 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); |
366 | 381 |
367 // Need to flush the GL commands before we return the tnumbnail texture to | 382 // Need to flush the GL commands before we return the tnumbnail texture to |
368 // the decoder. | 383 // the decoder. |
369 glFlush(); | 384 glFlush(); |
370 ++frame_count_; | 385 ++frame_count_; |
371 } | 386 } |
372 | 387 |
388 void RenderingHelper::QueueVideoFrame(size_t window_id, | |
389 scoped_refptr<VideoFrame> video_frame) { | |
390 RenderedVideo* video = &videos_[window_id]; | |
391 | |
392 // Pop the front if it has been rendered. | |
393 if (video->last_frame_rendered) { | |
394 DCHECK(!video->pending_frames.empty()); | |
Pawel Osciak
2014/08/15 05:45:16
Please comment why, this is not obvious.
Owen Lin
2014/08/18 09:03:08
Done.
| |
395 video->pending_frames.pop_front(); | |
396 video->last_frame_rendered = false; | |
Pawel Osciak
2014/08/15 05:45:16
By the way, for the future, we should have some th
Owen Lin
2014/08/18 09:03:08
Acknowledged.
| |
397 } | |
398 | |
399 video->pending_frames.push_back(video_frame); | |
400 } | |
401 | |
402 void RenderingHelper::DropPendingFrames(size_t window_id) { | |
403 RenderedVideo* video = &videos_[window_id]; | |
404 video->pending_frames.clear(); | |
405 video->last_frame_rendered = false; | |
406 } | |
407 | |
373 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { | 408 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { |
374 // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler | 409 // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler |
375 // is bound to GL_TEXTURE0. | 410 // is bound to GL_TEXTURE0. |
376 if (texture_target == GL_TEXTURE_2D) { | 411 if (texture_target == GL_TEXTURE_2D) { |
377 glActiveTexture(GL_TEXTURE0 + 0); | 412 glActiveTexture(GL_TEXTURE0 + 0); |
378 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) { | 413 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) { |
379 glActiveTexture(GL_TEXTURE0 + 1); | 414 glActiveTexture(GL_TEXTURE0 + 1); |
380 } | 415 } |
381 glBindTexture(texture_target, texture_id); | 416 glBindTexture(texture_target, texture_id); |
382 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 417 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
383 glBindTexture(texture_target, 0); | 418 glBindTexture(texture_target, 0); |
384 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 419 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
385 } | 420 } |
386 | 421 |
387 void RenderingHelper::DeleteTexture(uint32 texture_id) { | 422 void RenderingHelper::DeleteTexture(uint32 texture_id) { |
388 glDeleteTextures(1, &texture_id); | 423 glDeleteTextures(1, &texture_id); |
389 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 424 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
390 } | 425 } |
391 | 426 |
392 void* RenderingHelper::GetGLContext() { | 427 void* RenderingHelper::GetGLContext() { |
393 return gl_context_->GetHandle(); | 428 return gl_context_->GetHandle(); |
394 } | 429 } |
395 | 430 |
396 void* RenderingHelper::GetGLDisplay() { | 431 void* RenderingHelper::GetGLDisplay() { |
397 return gl_surface_->GetDisplay(); | 432 return gl_surface_->GetDisplay(); |
398 } | 433 } |
399 | 434 |
400 void RenderingHelper::Clear() { | 435 void RenderingHelper::Clear() { |
401 clients_.clear(); | 436 videos_.clear(); |
402 message_loop_ = NULL; | 437 message_loop_ = NULL; |
403 gl_context_ = NULL; | 438 gl_context_ = NULL; |
404 gl_surface_ = NULL; | 439 gl_surface_ = NULL; |
405 | 440 |
406 render_as_thumbnails_ = false; | 441 render_as_thumbnails_ = false; |
407 frame_count_ = 0; | 442 frame_count_ = 0; |
408 thumbnails_fbo_id_ = 0; | 443 thumbnails_fbo_id_ = 0; |
409 thumbnails_texture_id_ = 0; | 444 thumbnails_texture_id_ = 0; |
410 | 445 |
411 #if defined(OS_WIN) | 446 #if defined(OS_WIN) |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
454 } | 489 } |
455 *alpha_solid = solid; | 490 *alpha_solid = solid; |
456 | 491 |
457 done->Signal(); | 492 done->Signal(); |
458 } | 493 } |
459 | 494 |
460 void RenderingHelper::RenderContent() { | 495 void RenderingHelper::RenderContent() { |
461 CHECK_EQ(base::MessageLoop::current(), message_loop_); | 496 CHECK_EQ(base::MessageLoop::current(), message_loop_); |
462 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); | 497 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); |
463 | 498 |
499 // To keep the frame before SwapBuffers() | |
Pawel Osciak
2014/08/15 05:45:16
This is a bit too vague. Maybe: "Frames that will
Owen Lin
2014/08/18 09:03:08
The same comment was added to the member "last_fra
| |
500 std::vector<scoped_refptr<VideoFrame> > frames_to_be_returned; | |
501 | |
464 if (render_as_thumbnails_) { | 502 if (render_as_thumbnails_) { |
465 // In render_as_thumbnails_ mode, we render the FBO content on the | 503 // In render_as_thumbnails_ mode, we render the FBO content on the |
466 // screen instead of the decoded textures. | 504 // screen instead of the decoded textures. |
467 GLSetViewPort(render_areas_[0]); | 505 GLSetViewPort(videos_[0].render_area); |
468 RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); | 506 RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); |
469 } else { | 507 } else { |
470 for (size_t i = 0; i < clients_.size(); ++i) { | 508 for (size_t i = 0; i < videos_.size(); ++i) { |
471 if (clients_[i]) { | 509 RenderedVideo* video = &videos_[i]; |
472 GLSetViewPort(render_areas_[i]); | 510 if (video->pending_frames.empty()) |
473 clients_[i]->RenderContent(this); | 511 continue; |
512 scoped_refptr<VideoFrame> frame = video->pending_frames.front(); | |
513 GLSetViewPort(video->render_area); | |
514 RenderTexture(frame->texture_target(), frame->texture_id()); | |
515 | |
516 if (video->pending_frames.size() > 1) { | |
517 frames_to_be_returned.push_back(video->pending_frames.front()); | |
Pawel Osciak
2014/08/15 05:45:16
s/video->pending_frames.front()/frame/
Owen Lin
2014/08/18 09:03:08
Done.
| |
518 video->pending_frames.pop_front(); | |
519 } else { | |
520 video->last_frame_rendered = true; | |
Pawel Osciak
2014/08/15 05:45:16
Isn't this a problem in QueueVideoFrame? We may re
Owen Lin
2014/08/18 09:03:08
No, it won't. We set video_last_frame_rendered = t
Pawel Osciak
2014/08/20 10:26:26
Ok now I know why I misunderstood this. You are us
Owen Lin
2014/08/21 03:45:18
We will keep at most one frame for delayed decodin
| |
474 } | 521 } |
475 } | 522 } |
476 } | 523 } |
477 | 524 |
478 gl_surface_->SwapBuffers(); | 525 gl_surface_->SwapBuffers(); |
479 } | 526 } |
480 | 527 |
481 // Helper function for the LayoutRenderingAreas(). The |lengths| are the | 528 // Helper function for the LayoutRenderingAreas(). The |lengths| are the |
482 // heights(widths) of the rows(columns). It scales the elements in | 529 // heights(widths) of the rows(columns). It scales the elements in |
483 // |lengths| proportionally so that the sum of them equal to |total_length|. | 530 // |lengths| proportionally so that the sum of them equal to |total_length|. |
484 // It also outputs the coordinates of the rows(columns) to |offsets|. | 531 // It also outputs the coordinates of the rows(columns) to |offsets|. |
485 static void ScaleAndCalculateOffsets(std::vector<int>* lengths, | 532 static void ScaleAndCalculateOffsets(std::vector<int>* lengths, |
486 std::vector<int>* offsets, | 533 std::vector<int>* offsets, |
487 int total_length) { | 534 int total_length) { |
488 int sum = std::accumulate(lengths->begin(), lengths->end(), 0); | 535 int sum = std::accumulate(lengths->begin(), lengths->end(), 0); |
489 for (size_t i = 0; i < lengths->size(); ++i) { | 536 for (size_t i = 0; i < lengths->size(); ++i) { |
490 lengths->at(i) = lengths->at(i) * total_length / sum; | 537 lengths->at(i) = lengths->at(i) * total_length / sum; |
491 offsets->at(i) = (i == 0) ? 0 : offsets->at(i - 1) + lengths->at(i - 1); | 538 offsets->at(i) = (i == 0) ? 0 : offsets->at(i - 1) + lengths->at(i - 1); |
492 } | 539 } |
493 } | 540 } |
494 | 541 |
495 void RenderingHelper::LayoutRenderingAreas() { | 542 void RenderingHelper::LayoutRenderingAreas( |
543 const std::vector<gfx::Size>& window_sizes) { | |
496 // Find the number of colums and rows. | 544 // Find the number of colums and rows. |
497 // The smallest n * n or n * (n + 1) > number of clients. | 545 // The smallest n * n or n * (n + 1) > number of windows. |
498 size_t cols = sqrt(clients_.size() - 1) + 1; | 546 size_t cols = sqrt(videos_.size() - 1) + 1; |
499 size_t rows = (clients_.size() + cols - 1) / cols; | 547 size_t rows = (videos_.size() + cols - 1) / cols; |
500 | 548 |
501 // Find the widths and heights of the grid. | 549 // Find the widths and heights of the grid. |
502 std::vector<int> widths(cols); | 550 std::vector<int> widths(cols); |
503 std::vector<int> heights(rows); | 551 std::vector<int> heights(rows); |
504 std::vector<int> offset_x(cols); | 552 std::vector<int> offset_x(cols); |
505 std::vector<int> offset_y(rows); | 553 std::vector<int> offset_y(rows); |
506 | 554 |
507 for (size_t i = 0; i < clients_.size(); ++i) { | 555 for (size_t i = 0; i < window_sizes.size(); ++i) { |
508 const gfx::Size& window_size = clients_[i]->GetWindowSize(); | 556 const gfx::Size& size = window_sizes[i]; |
509 widths[i % cols] = std::max(widths[i % cols], window_size.width()); | 557 widths[i % cols] = std::max(widths[i % cols], size.width()); |
510 heights[i / cols] = std::max(heights[i / cols], window_size.height()); | 558 heights[i / cols] = std::max(heights[i / cols], size.height()); |
511 } | 559 } |
512 | 560 |
513 ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width()); | 561 ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width()); |
514 ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height()); | 562 ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height()); |
515 | 563 |
516 // Put each render_area_ in the center of each cell. | 564 // Put each render_area_ in the center of each cell. |
517 render_areas_.clear(); | 565 for (size_t i = 0; i < window_sizes.size(); ++i) { |
518 for (size_t i = 0; i < clients_.size(); ++i) { | 566 const gfx::Size& size = window_sizes[i]; |
519 const gfx::Size& window_size = clients_[i]->GetWindowSize(); | |
520 float scale = | 567 float scale = |
521 std::min(static_cast<float>(widths[i % cols]) / window_size.width(), | 568 std::min(static_cast<float>(widths[i % cols]) / size.width(), |
522 static_cast<float>(heights[i / cols]) / window_size.height()); | 569 static_cast<float>(heights[i / cols]) / size.height()); |
523 | 570 |
524 // Don't scale up the texture. | 571 // Don't scale up the texture. |
525 scale = std::min(1.0f, scale); | 572 scale = std::min(1.0f, scale); |
526 | 573 |
527 size_t w = scale * window_size.width(); | 574 size_t w = scale * size.width(); |
528 size_t h = scale * window_size.height(); | 575 size_t h = scale * size.height(); |
529 size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2; | 576 size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2; |
530 size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2; | 577 size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2; |
531 render_areas_.push_back(gfx::Rect(x, y, w, h)); | 578 videos_[i].render_area = gfx::Rect(x, y, w, h); |
532 } | 579 } |
533 } | 580 } |
534 } // namespace content | 581 } // namespace content |
OLD | NEW |