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
12namespace content {
13
14RTCVideoRenderer::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
26RTCVideoRenderer::~RTCVideoRenderer() {
27}
28
29void 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
44void 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
52void RTCVideoRenderer::Play() {
53  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
54  if (state_ == PAUSED) {
55    state_ = STARTED;
56  }
57}
58
59void RTCVideoRenderer::Pause() {
60  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
61  if (state_ == STARTED) {
62    state_ = PAUSED;
63  }
64}
65
66void RTCVideoRenderer::OnReadyStateChanged(
67    blink::WebMediaStreamSource::ReadyState state) {
68  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
69  if (state == blink::WebMediaStreamSource::ReadyStateEnded)
70    MaybeRenderSignalingFrame();
71}
72
73void RTCVideoRenderer::OnEnabledChanged(bool enabled) {
74  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
75  if (!enabled)
76    MaybeRenderSignalingFrame();
77}
78
79void 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
95void 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