1 // Copyright (c) 2012 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 "content/renderer/media/rtc_video_renderer.h" 6 7 #include "base/bind.h" 8 #include "base/debug/trace_event.h" 9 #include "base/location.h" 10 #include "base/logging.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "media/base/video_frame.h" 13 #include "media/base/video_util.h" 14 #include "third_party/libjingle/source/talk/media/base/videoframe.h" 15 16 using media::CopyYPlane; 17 using media::CopyUPlane; 18 using media::CopyVPlane; 19 20 namespace content { 21 22 RTCVideoRenderer::RTCVideoRenderer( 23 webrtc::VideoTrackInterface* video_track, 24 const base::Closure& error_cb, 25 const RepaintCB& repaint_cb) 26 : error_cb_(error_cb), 27 repaint_cb_(repaint_cb), 28 message_loop_proxy_(base::MessageLoopProxy::current()), 29 state_(kStopped), 30 video_track_(video_track) { 31 } 32 33 RTCVideoRenderer::~RTCVideoRenderer() { 34 } 35 36 void RTCVideoRenderer::Start() { 37 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 38 DCHECK_EQ(state_, kStopped); 39 40 if (video_track_.get()) { 41 video_track_->AddRenderer(this); 42 video_track_->RegisterObserver(this); 43 } 44 state_ = kStarted; 45 MaybeRenderSignalingFrame(); 46 } 47 48 void RTCVideoRenderer::Stop() { 49 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 50 if (video_track_.get()) { 51 state_ = kStopped; 52 video_track_->RemoveRenderer(this); 53 video_track_->UnregisterObserver(this); 54 video_track_ = NULL; 55 } 56 } 57 58 void RTCVideoRenderer::Play() { 59 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 60 if (video_track_.get() && state_ == kPaused) { 61 state_ = kStarted; 62 } 63 } 64 65 void RTCVideoRenderer::Pause() { 66 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 67 if (video_track_.get() && state_ == kStarted) { 68 state_ = kPaused; 69 } 70 } 71 72 void RTCVideoRenderer::SetSize(int width, int height) { 73 } 74 75 void RTCVideoRenderer::RenderFrame(const cricket::VideoFrame* frame) { 76 base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( 77 frame->GetTimeStamp() / talk_base::kNumNanosecsPerMillisec); 78 79 TRACE_EVENT_INSTANT2("rtc_video_renderer", 80 "RenderFrame", 81 TRACE_EVENT_SCOPE_THREAD, 82 "elapsed time", 83 frame->GetElapsedTime(), 84 "timestamp_ms", 85 timestamp.InMilliseconds()); 86 87 gfx::Size size(frame->GetWidth(), frame->GetHeight()); 88 scoped_refptr<media::VideoFrame> video_frame = 89 media::VideoFrame::CreateFrame(media::VideoFrame::YV12, 90 size, 91 gfx::Rect(size), 92 size, 93 timestamp); 94 95 // Aspect ratio unsupported; DCHECK when there are non-square pixels. 96 DCHECK_EQ(frame->GetPixelWidth(), 1u); 97 DCHECK_EQ(frame->GetPixelHeight(), 1u); 98 99 int y_rows = frame->GetHeight(); 100 int uv_rows = frame->GetHeight() / 2; // YV12 format. 101 CopyYPlane(frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame.get()); 102 CopyUPlane( 103 frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame.get()); 104 CopyVPlane( 105 frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame.get()); 106 107 message_loop_proxy_->PostTask( 108 FROM_HERE, base::Bind(&RTCVideoRenderer::DoRenderFrameOnMainThread, 109 this, video_frame)); 110 } 111 112 void RTCVideoRenderer::OnChanged() { 113 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 114 MaybeRenderSignalingFrame(); 115 } 116 117 void RTCVideoRenderer::MaybeRenderSignalingFrame() { 118 // Render a small black frame if the track transition to ended. 119 // This is necessary to make sure audio can play if the video tag src is 120 // a MediaStream video track that has been rejected or ended. 121 if (video_track_->state() == webrtc::MediaStreamTrackInterface::kEnded) { 122 const int kMinFrameSize = 2; 123 const gfx::Size size(kMinFrameSize, kMinFrameSize); 124 scoped_refptr<media::VideoFrame> video_frame = 125 media::VideoFrame::CreateBlackFrame(size); 126 DoRenderFrameOnMainThread(video_frame); 127 } 128 } 129 130 void RTCVideoRenderer::DoRenderFrameOnMainThread( 131 scoped_refptr<media::VideoFrame> video_frame) { 132 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); 133 134 if (state_ != kStarted) { 135 return; 136 } 137 138 TRACE_EVENT0("video", "DoRenderFrameOnMainThread"); 139 repaint_cb_.Run(video_frame); 140 } 141 142 } // namespace content 143