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/debug/trace_event.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "media/base/video_frame.h"
10 #include "media/base/video_util.h"
11
12 namespace content {
13
RTCVideoRenderer(const blink::WebMediaStreamTrack & video_track,const base::Closure & error_cb,const RepaintCB & repaint_cb)14 RTCVideoRenderer::RTCVideoRenderer(
15 const blink::WebMediaStreamTrack& video_track,
16 const base::Closure& error_cb,
17 const RepaintCB& repaint_cb)
18 : error_cb_(error_cb),
19 repaint_cb_(repaint_cb),
20 message_loop_proxy_(base::MessageLoopProxy::current()),
21 state_(STOPPED),
22 first_frame_rendered_(false),
23 video_track_(video_track) {
24 }
25
~RTCVideoRenderer()26 RTCVideoRenderer::~RTCVideoRenderer() {
27 }
28
Start()29 void RTCVideoRenderer::Start() {
30 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
31 DCHECK_EQ(state_, STOPPED);
32 DCHECK(!first_frame_rendered_);
33
34 AddToVideoTrack(this, video_track_);
35 state_ = STARTED;
36
37 if (video_track_.source().readyState() ==
38 blink::WebMediaStreamSource::ReadyStateEnded ||
39 !video_track_.isEnabled()) {
40 MaybeRenderSignalingFrame();
41 }
42 }
43
Stop()44 void RTCVideoRenderer::Stop() {
45 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
46 DCHECK(state_ == STARTED || state_ == PAUSED);
47 RemoveFromVideoTrack(this, video_track_);
48 state_ = STOPPED;
49 first_frame_rendered_ = false;
50 }
51
Play()52 void RTCVideoRenderer::Play() {
53 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
54 if (state_ == PAUSED) {
55 state_ = STARTED;
56 }
57 }
58
Pause()59 void RTCVideoRenderer::Pause() {
60 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
61 if (state_ == STARTED) {
62 state_ = PAUSED;
63 }
64 }
65
OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state)66 void RTCVideoRenderer::OnReadyStateChanged(
67 blink::WebMediaStreamSource::ReadyState state) {
68 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
69 if (state == blink::WebMediaStreamSource::ReadyStateEnded)
70 MaybeRenderSignalingFrame();
71 }
72
OnEnabledChanged(bool enabled)73 void RTCVideoRenderer::OnEnabledChanged(bool enabled) {
74 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
75 if (!enabled)
76 MaybeRenderSignalingFrame();
77 }
78
OnVideoFrame(const scoped_refptr<media::VideoFrame> & frame)79 void RTCVideoRenderer::OnVideoFrame(
80 const scoped_refptr<media::VideoFrame>& frame) {
81 DCHECK(message_loop_proxy_->BelongsToCurrentThread());
82 if (state_ != STARTED) {
83 return;
84 }
85
86 TRACE_EVENT_INSTANT1("rtc_video_renderer",
87 "OnVideoFrame",
88 TRACE_EVENT_SCOPE_THREAD,
89 "timestamp",
90 frame->GetTimestamp().InMilliseconds());
91 repaint_cb_.Run(frame);
92 first_frame_rendered_ = true;
93 }
94
MaybeRenderSignalingFrame()95 void RTCVideoRenderer::MaybeRenderSignalingFrame() {
96 // Render a small black frame if no frame has been rendered.
97 // This is necessary to make sure audio can play if the video tag src is
98 // a MediaStream video track that has been rejected, ended or disabled.
99 if (first_frame_rendered_)
100 return;
101
102 const int kMinFrameSize = 2;
103 const gfx::Size size(kMinFrameSize, kMinFrameSize);
104 scoped_refptr<media::VideoFrame> video_frame =
105 media::VideoFrame::CreateBlackFrame(size);
106 OnVideoFrame(video_frame);
107 }
108
109 } // namespace content
110