Chromium Code Reviews| 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 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 texture_id_(texture_id), | 69 texture_id_(texture_id), |
| 70 no_longer_needed_cb_(no_longer_needed_cb) { | 70 no_longer_needed_cb_(no_longer_needed_cb) { |
| 71 DCHECK(!no_longer_needed_cb_.is_null()); | 71 DCHECK(!no_longer_needed_cb_.is_null()); |
| 72 } | 72 } |
| 73 | 73 |
| 74 VideoFrameTexture::~VideoFrameTexture() { | 74 VideoFrameTexture::~VideoFrameTexture() { |
| 75 base::ResetAndReturn(&no_longer_needed_cb_).Run(); | 75 base::ResetAndReturn(&no_longer_needed_cb_).Run(); |
| 76 } | 76 } |
| 77 | 77 |
| 78 RenderingHelper::RenderedVideo::RenderedVideo() | 78 RenderingHelper::RenderedVideo::RenderedVideo() |
| 79 : last_frame_rendered(false), is_flushing(false), frames_to_drop(0) { | 79 : is_flushing(false), frames_to_drop(0) { |
| 80 } | 80 } |
| 81 | 81 |
| 82 RenderingHelper::RenderedVideo::~RenderedVideo() { | 82 RenderingHelper::RenderedVideo::~RenderedVideo() { |
| 83 } | 83 } |
| 84 | 84 |
| 85 // static | 85 // static |
| 86 bool RenderingHelper::InitializeOneOff() { | 86 bool RenderingHelper::InitializeOneOff() { |
| 87 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); | 87 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 88 #if GL_VARIANT_GLX | 88 #if GL_VARIANT_GLX |
| 89 cmd_line->AppendSwitchASCII(switches::kUseGL, | 89 cmd_line->AppendSwitchASCII(switches::kUseGL, |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 295 if (tex_external != -1) { | 295 if (tex_external != -1) { |
| 296 glUniform1i(tex_external, 1); | 296 glUniform1i(tex_external, 1); |
| 297 } | 297 } |
| 298 int pos_location = glGetAttribLocation(program_, "in_pos"); | 298 int pos_location = glGetAttribLocation(program_, "in_pos"); |
| 299 glEnableVertexAttribArray(pos_location); | 299 glEnableVertexAttribArray(pos_location); |
| 300 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); | 300 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); |
| 301 int tc_location = glGetAttribLocation(program_, "in_tc"); | 301 int tc_location = glGetAttribLocation(program_, "in_tc"); |
| 302 glEnableVertexAttribArray(tc_location); | 302 glEnableVertexAttribArray(tc_location); |
| 303 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords); | 303 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords); |
| 304 | 304 |
| 305 done->Signal(); | 305 gl_surface_->GetVSyncProvider()->GetVSyncParameters(base::Bind( |
| 306 &RenderingHelper::UpdateVSyncParameters, base::Unretained(this), done)); | |
|
piman
2014/10/16 21:13:29
Can you document why Unretained is safe?
Owen Lin
2014/10/20 08:48:42
Done.
| |
| 306 } | 307 } |
| 307 | 308 |
| 308 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { | 309 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { |
| 309 CHECK_EQ(base::MessageLoop::current(), message_loop_); | 310 CHECK_EQ(base::MessageLoop::current(), message_loop_); |
| 310 | 311 |
| 311 render_task_.Cancel(); | 312 render_task_.Cancel(); |
| 312 | 313 |
| 313 if (render_as_thumbnails_) { | 314 if (render_as_thumbnails_) { |
| 314 glDeleteTextures(1, &thumbnails_texture_id_); | 315 glDeleteTextures(1, &thumbnails_texture_id_); |
| 315 glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_); | 316 glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 389 ++frame_count_; | 390 ++frame_count_; |
| 390 } | 391 } |
| 391 | 392 |
| 392 void RenderingHelper::QueueVideoFrame( | 393 void RenderingHelper::QueueVideoFrame( |
| 393 size_t window_id, | 394 size_t window_id, |
| 394 scoped_refptr<VideoFrameTexture> video_frame) { | 395 scoped_refptr<VideoFrameTexture> video_frame) { |
| 395 CHECK_EQ(base::MessageLoop::current(), message_loop_); | 396 CHECK_EQ(base::MessageLoop::current(), message_loop_); |
| 396 RenderedVideo* video = &videos_[window_id]; | 397 RenderedVideo* video = &videos_[window_id]; |
| 397 DCHECK(!video->is_flushing); | 398 DCHECK(!video->is_flushing); |
| 398 | 399 |
| 399 // Start the rendering task when getting the first frame. | 400 video->pending_frames.push(video_frame); |
| 400 if (scheduled_render_time_.is_null() && | 401 |
| 401 (frame_duration_ != base::TimeDelta())) { | 402 if (video->frames_to_drop > 0 && video->pending_frames.size() > 1) { |
| 403 --video->frames_to_drop; | |
| 404 video->pending_frames.pop(); | |
| 405 } | |
| 406 | |
| 407 // Schedules the first RenderContent() if need. | |
| 408 if (scheduled_render_time_.is_null()) { | |
| 402 scheduled_render_time_ = base::TimeTicks::Now(); | 409 scheduled_render_time_ = base::TimeTicks::Now(); |
| 403 message_loop_->PostTask(FROM_HERE, render_task_.callback()); | 410 message_loop_->PostTask(FROM_HERE, render_task_.callback()); |
| 404 } | 411 } |
| 405 | |
| 406 if (video->frames_to_drop > 0) { | |
| 407 --video->frames_to_drop; | |
| 408 return; | |
| 409 } | |
| 410 | |
| 411 // Pop the last frame if it has been rendered. | |
| 412 if (video->last_frame_rendered) { | |
| 413 // When last_frame_rendered is true, we should have only one pending frame. | |
| 414 // Since we are going to have a new frame, we can release the pending one. | |
| 415 DCHECK(video->pending_frames.size() == 1); | |
| 416 video->pending_frames.pop(); | |
| 417 video->last_frame_rendered = false; | |
| 418 } | |
| 419 | |
| 420 video->pending_frames.push(video_frame); | |
| 421 } | 412 } |
| 422 | 413 |
| 423 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { | 414 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { |
| 424 // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler | 415 // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler |
| 425 // is bound to GL_TEXTURE0. | 416 // is bound to GL_TEXTURE0. |
| 426 if (texture_target == GL_TEXTURE_2D) { | 417 if (texture_target == GL_TEXTURE_2D) { |
| 427 glActiveTexture(GL_TEXTURE0 + 0); | 418 glActiveTexture(GL_TEXTURE0 + 0); |
| 428 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) { | 419 } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) { |
| 429 glActiveTexture(GL_TEXTURE0 + 1); | 420 glActiveTexture(GL_TEXTURE0 + 1); |
| 430 } | 421 } |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 508 done->Signal(); | 499 done->Signal(); |
| 509 } | 500 } |
| 510 | 501 |
| 511 void RenderingHelper::Flush(size_t window_id) { | 502 void RenderingHelper::Flush(size_t window_id) { |
| 512 videos_[window_id].is_flushing = true; | 503 videos_[window_id].is_flushing = true; |
| 513 } | 504 } |
| 514 | 505 |
| 515 void RenderingHelper::RenderContent() { | 506 void RenderingHelper::RenderContent() { |
| 516 CHECK_EQ(base::MessageLoop::current(), message_loop_); | 507 CHECK_EQ(base::MessageLoop::current(), message_loop_); |
| 517 | 508 |
| 518 scheduled_render_time_ += frame_duration_; | 509 // Update the VSync params. |
| 519 base::TimeDelta delay = scheduled_render_time_ - base::TimeTicks::Now(); | 510 gl_surface_->GetVSyncProvider()->GetVSyncParameters( |
| 520 message_loop_->PostDelayedTask( | 511 base::Bind(&RenderingHelper::UpdateVSyncParameters, |
| 521 FROM_HERE, render_task_.callback(), std::max(delay, base::TimeDelta())); | 512 base::Unretained(this), |
|
piman
2014/10/16 21:13:28
Can you document why Unretained is safe?
Owen Lin
2014/10/20 08:48:42
Done.
| |
| 513 static_cast<base::WaitableEvent*>(NULL))); | |
| 522 | 514 |
| 523 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); | 515 glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); |
| 524 | 516 |
| 525 // Frames that will be returned to the client (via the no_longer_needed_cb) | 517 // Frames that will be returned to the client (via the no_longer_needed_cb) |
| 526 // after this vector falls out of scope at the end of this method. We need | 518 // after this vector falls out of scope at the end of this method. We need |
| 527 // to keep references to them until after SwapBuffers() call below. | 519 // to keep references to them until after SwapBuffers() call below. |
| 528 std::vector<scoped_refptr<VideoFrameTexture> > frames_to_be_returned; | 520 std::vector<scoped_refptr<VideoFrameTexture> > frames_to_be_returned; |
| 529 | 521 bool need_swap_buffer = false; |
| 530 if (render_as_thumbnails_) { | 522 if (render_as_thumbnails_) { |
| 531 // In render_as_thumbnails_ mode, we render the FBO content on the | 523 // In render_as_thumbnails_ mode, we render the FBO content on the |
| 532 // screen instead of the decoded textures. | 524 // screen instead of the decoded textures. |
| 533 GLSetViewPort(videos_[0].render_area); | 525 GLSetViewPort(videos_[0].render_area); |
| 534 RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); | 526 RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); |
| 527 need_swap_buffer = true; | |
| 535 } else { | 528 } else { |
| 536 for (size_t i = 0; i < videos_.size(); ++i) { | 529 for (size_t i = 0; i < videos_.size(); ++i) { |
| 537 RenderedVideo* video = &videos_[i]; | 530 RenderedVideo* video = &videos_[i]; |
| 538 if (video->pending_frames.empty()) | 531 if (video->pending_frames.empty()) |
| 539 continue; | 532 continue; |
| 533 need_swap_buffer = true; | |
| 540 scoped_refptr<VideoFrameTexture> frame = video->pending_frames.front(); | 534 scoped_refptr<VideoFrameTexture> frame = video->pending_frames.front(); |
| 541 GLSetViewPort(video->render_area); | 535 GLSetViewPort(video->render_area); |
| 542 RenderTexture(frame->texture_target(), frame->texture_id()); | 536 RenderTexture(frame->texture_target(), frame->texture_id()); |
| 543 | 537 |
| 544 if (video->last_frame_rendered) | |
| 545 ++video->frames_to_drop; | |
| 546 | |
| 547 if (video->pending_frames.size() > 1 || video->is_flushing) { | 538 if (video->pending_frames.size() > 1 || video->is_flushing) { |
| 548 frames_to_be_returned.push_back(video->pending_frames.front()); | 539 frames_to_be_returned.push_back(video->pending_frames.front()); |
| 549 video->pending_frames.pop(); | 540 video->pending_frames.pop(); |
| 550 video->last_frame_rendered = false; | |
| 551 } else { | 541 } else { |
| 552 video->last_frame_rendered = true; | 542 ++video->frames_to_drop; |
| 553 } | 543 } |
| 554 } | 544 } |
| 555 } | 545 } |
| 556 | 546 |
| 557 gl_surface_->SwapBuffers(); | 547 if (need_swap_buffer) |
| 548 gl_surface_->SwapBuffers(); | |
| 549 | |
| 550 ScheduleNextRenderContent(); | |
| 558 } | 551 } |
| 559 | 552 |
| 560 // Helper function for the LayoutRenderingAreas(). The |lengths| are the | 553 // Helper function for the LayoutRenderingAreas(). The |lengths| are the |
| 561 // heights(widths) of the rows(columns). It scales the elements in | 554 // heights(widths) of the rows(columns). It scales the elements in |
| 562 // |lengths| proportionally so that the sum of them equal to |total_length|. | 555 // |lengths| proportionally so that the sum of them equal to |total_length|. |
| 563 // It also outputs the coordinates of the rows(columns) to |offsets|. | 556 // It also outputs the coordinates of the rows(columns) to |offsets|. |
| 564 static void ScaleAndCalculateOffsets(std::vector<int>* lengths, | 557 static void ScaleAndCalculateOffsets(std::vector<int>* lengths, |
| 565 std::vector<int>* offsets, | 558 std::vector<int>* offsets, |
| 566 int total_length) { | 559 int total_length) { |
| 567 int sum = std::accumulate(lengths->begin(), lengths->end(), 0); | 560 int sum = std::accumulate(lengths->begin(), lengths->end(), 0); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 603 // Don't scale up the texture. | 596 // Don't scale up the texture. |
| 604 scale = std::min(1.0f, scale); | 597 scale = std::min(1.0f, scale); |
| 605 | 598 |
| 606 size_t w = scale * size.width(); | 599 size_t w = scale * size.width(); |
| 607 size_t h = scale * size.height(); | 600 size_t h = scale * size.height(); |
| 608 size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2; | 601 size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2; |
| 609 size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2; | 602 size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2; |
| 610 videos_[i].render_area = gfx::Rect(x, y, w, h); | 603 videos_[i].render_area = gfx::Rect(x, y, w, h); |
| 611 } | 604 } |
| 612 } | 605 } |
| 606 | |
| 607 void RenderingHelper::UpdateVSyncParameters(base::WaitableEvent* done, | |
| 608 const base::TimeTicks timebase, | |
| 609 const base::TimeDelta interval) { | |
| 610 vsync_timebase_ = timebase; | |
| 611 vsync_interval_ = interval; | |
| 612 | |
| 613 if (done) | |
| 614 done->Signal(); | |
| 615 } | |
| 616 | |
| 617 void RenderingHelper::DropOneFrameForAllVideos() { | |
| 618 for (size_t i = 0; i < videos_.size(); ++i) { | |
| 619 RenderedVideo* video = &videos_[i]; | |
|
piman
2014/10/16 21:13:28
nit: for (RenderedVideo& video : videos_) {
Owen Lin
2014/10/20 08:48:42
Done. Thanks
| |
| 620 if (video->pending_frames.empty()) | |
| 621 continue; | |
| 622 | |
| 623 if (video->pending_frames.size() > 1 || video->is_flushing) { | |
| 624 video->pending_frames.pop(); | |
| 625 } else { | |
| 626 ++video->frames_to_drop; | |
| 627 } | |
| 628 } | |
| 629 } | |
| 630 | |
| 631 void RenderingHelper::ScheduleNextRenderContent() { | |
| 632 scheduled_render_time_ += frame_duration_; | |
| 633 | |
| 634 // Schedules the next RenderContent() at latest VSYNC before the | |
| 635 // |scheuled_render_time_|. | |
|
piman
2014/10/16 21:13:28
nit: typo scheduled_render_time_
Owen Lin
2014/10/20 08:48:42
Done.
| |
| 636 base::TimeTicks now = base::TimeTicks::Now(); | |
| 637 base::TimeTicks target = | |
| 638 std::max(now, scheduled_render_time_ - vsync_interval_); | |
| 639 | |
| 640 while (vsync_timebase_ < target) | |
| 641 vsync_timebase_ += vsync_interval_; | |
|
piman
2014/10/16 21:13:28
vsync_timebase_ may not be close to the current ti
Owen Lin
2014/10/20 08:48:42
Thanks.
| |
| 642 | |
| 643 // When the rendering falls behind, drops frames. | |
| 644 while (scheduled_render_time_ < vsync_timebase_) { | |
| 645 scheduled_render_time_ += frame_duration_; | |
| 646 DropOneFrameForAllVideos(); | |
| 647 } | |
| 648 | |
| 649 message_loop_->PostDelayedTask( | |
| 650 FROM_HERE, render_task_.callback(), vsync_timebase_ - now); | |
| 651 } | |
| 613 } // namespace content | 652 } // namespace content |
| OLD | NEW |