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 |