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/bind_to_current_loop.h"
10#include "media/base/video_frame.h"
11#include "media/base/video_util.h"
12
13const int kMinFrameSize = 2;
14
15namespace content {
16
17RTCVideoRenderer::RTCVideoRenderer(
18    const blink::WebMediaStreamTrack& video_track,
19    const base::Closure& error_cb,
20    const RepaintCB& repaint_cb)
21    : error_cb_(error_cb),
22      repaint_cb_(repaint_cb),
23      message_loop_proxy_(base::MessageLoopProxy::current()),
24      state_(STOPPED),
25      frame_size_(kMinFrameSize, kMinFrameSize),
26      video_track_(video_track),
27      weak_factory_(this) {
28}
29
30RTCVideoRenderer::~RTCVideoRenderer() {
31}
32
33void RTCVideoRenderer::Start() {
34  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
35  DCHECK_EQ(state_, STOPPED);
36
37  AddToVideoTrack(
38      this,
39      media::BindToCurrentLoop(
40          base::Bind(
41              &RTCVideoRenderer::OnVideoFrame,
42              weak_factory_.GetWeakPtr())),
43      video_track_);
44  state_ = STARTED;
45
46  if (video_track_.source().readyState() ==
47          blink::WebMediaStreamSource::ReadyStateEnded ||
48      !video_track_.isEnabled()) {
49    RenderSignalingFrame();
50  }
51}
52
53void RTCVideoRenderer::Stop() {
54  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
55  DCHECK(state_ == STARTED || state_ == PAUSED);
56  RemoveFromVideoTrack(this, video_track_);
57  weak_factory_.InvalidateWeakPtrs();
58  state_ = STOPPED;
59  frame_size_.set_width(kMinFrameSize);
60  frame_size_.set_height(kMinFrameSize);
61}
62
63void RTCVideoRenderer::Play() {
64  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
65  if (state_ == PAUSED) {
66    state_ = STARTED;
67  }
68}
69
70void RTCVideoRenderer::Pause() {
71  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
72  if (state_ == STARTED) {
73    state_ = PAUSED;
74  }
75}
76
77void RTCVideoRenderer::OnReadyStateChanged(
78    blink::WebMediaStreamSource::ReadyState state) {
79  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
80  if (state == blink::WebMediaStreamSource::ReadyStateEnded)
81    RenderSignalingFrame();
82}
83
84void RTCVideoRenderer::OnVideoFrame(
85    const scoped_refptr<media::VideoFrame>& frame,
86    const media::VideoCaptureFormat& format,
87    const base::TimeTicks& estimated_capture_time) {
88  DCHECK(message_loop_proxy_->BelongsToCurrentThread());
89  if (state_ != STARTED) {
90    return;
91  }
92
93  frame_size_ = frame->natural_size();
94
95  TRACE_EVENT_INSTANT1("rtc_video_renderer",
96                       "OnVideoFrame",
97                       TRACE_EVENT_SCOPE_THREAD,
98                       "timestamp",
99                       frame->timestamp().InMilliseconds());
100  repaint_cb_.Run(frame);
101}
102
103void RTCVideoRenderer::RenderSignalingFrame() {
104  // This is necessary to make sure audio can play if the video tag src is
105  // a MediaStream video track that has been rejected or ended.
106  // It also ensure that the renderer don't hold a reference to a real video
107  // frame if no more frames are provided. This is since there might be a
108  // finite number of available buffers. E.g, video that
109  // originates from a video camera.
110  scoped_refptr<media::VideoFrame> video_frame =
111      media::VideoFrame::CreateBlackFrame(frame_size_);
112  OnVideoFrame(video_frame, media::VideoCaptureFormat(), base::TimeTicks());
113}
114
115}  // namespace content
116