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
3// found in the LICENSE file.
4
5#include "content/renderer/media/media_stream_video_track.h"
6
7#include <utility>
8
9#include "base/bind.h"
10#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h"
11
12namespace content {
13
14namespace {
15void ResetCallback(scoped_ptr<VideoCaptureDeliverFrameCB> callback) {
16  // |callback| will be deleted when this exits.
17}
18}  // namespace
19
20// MediaStreamVideoTrack::FrameDeliverer is a helper class used for registering
21// VideoCaptureDeliverFrameCB on the main render thread to receive video frames
22// on the IO-thread.
23// Frames are only delivered to the sinks if the track is enabled. If the track
24// is disabled, a black frame is instead forwarded to the sinks at the same
25// frame rate.
26class MediaStreamVideoTrack::FrameDeliverer
27    : public base::RefCountedThreadSafe<FrameDeliverer> {
28 public:
29  FrameDeliverer(
30      const scoped_refptr<base::MessageLoopProxy>& io_message_loop,
31      bool enabled);
32
33  void SetEnabled(bool enabled);
34
35  // Add |callback| to receive video frames on the IO-thread.
36  // Must be called on the main render thread.
37  void AddCallback(void* id, const VideoCaptureDeliverFrameCB& callback);
38
39  // Removes |callback| associated with |id| from receiving video frames if |id|
40  // has been added. It is ok to call RemoveCallback even if the |id| has not
41  // been added. Note that the added callback will be reset on the main thread.
42  // Must be called on the main render thread.
43  void RemoveCallback(void* id);
44
45  // Triggers all registered callbacks with |frame|, |format| and
46  // |estimated_capture_time| as parameters. Must be called on the IO-thread.
47  void DeliverFrameOnIO(const scoped_refptr<media::VideoFrame>& frame,
48                        const media::VideoCaptureFormat& format,
49                        const base::TimeTicks& estimated_capture_time);
50
51 private:
52  friend class base::RefCountedThreadSafe<FrameDeliverer>;
53  virtual ~FrameDeliverer();
54  void AddCallbackOnIO(void* id, const VideoCaptureDeliverFrameCB& callback);
55  void RemoveCallbackOnIO(
56      void* id, const scoped_refptr<base::MessageLoopProxy>& message_loop);
57
58  void SetEnabledOnIO(bool enabled);
59  // Returns |black_frame_| where the size and time stamp is set to the same as
60  // as in |reference_frame|.
61  const scoped_refptr<media::VideoFrame>& GetBlackFrame(
62      const scoped_refptr<media::VideoFrame>& reference_frame);
63
64  // Used to DCHECK that AddCallback and RemoveCallback are called on the main
65  // render thread.
66  base::ThreadChecker thread_checker_;
67  scoped_refptr<base::MessageLoopProxy> io_message_loop_;
68
69  bool enabled_;
70  scoped_refptr<media::VideoFrame> black_frame_;
71
72  typedef std::pair<void*, VideoCaptureDeliverFrameCB> VideoIdCallbackPair;
73  std::vector<VideoIdCallbackPair> callbacks_;
74
75  DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
76};
77
78MediaStreamVideoTrack::FrameDeliverer::FrameDeliverer(
79    const scoped_refptr<base::MessageLoopProxy>& io_message_loop, bool enabled)
80    : io_message_loop_(io_message_loop),
81      enabled_(enabled) {
82  DCHECK(io_message_loop_.get());
83}
84
85MediaStreamVideoTrack::FrameDeliverer::~FrameDeliverer() {
86  DCHECK(callbacks_.empty());
87}
88
89void MediaStreamVideoTrack::FrameDeliverer::AddCallback(
90    void* id,
91    const VideoCaptureDeliverFrameCB& callback) {
92  DCHECK(thread_checker_.CalledOnValidThread());
93  io_message_loop_->PostTask(
94      FROM_HERE,
95      base::Bind(&FrameDeliverer::AddCallbackOnIO,
96                 this, id, callback));
97}
98
99void MediaStreamVideoTrack::FrameDeliverer::AddCallbackOnIO(
100    void* id,
101    const VideoCaptureDeliverFrameCB& callback) {
102  DCHECK(io_message_loop_->BelongsToCurrentThread());
103  callbacks_.push_back(std::make_pair(id, callback));
104}
105
106void MediaStreamVideoTrack::FrameDeliverer::RemoveCallback(void* id) {
107  DCHECK(thread_checker_.CalledOnValidThread());
108  io_message_loop_->PostTask(
109      FROM_HERE,
110      base::Bind(&FrameDeliverer::RemoveCallbackOnIO,
111                 this, id, base::MessageLoopProxy::current()));
112}
113
114void MediaStreamVideoTrack::FrameDeliverer::RemoveCallbackOnIO(
115    void* id, const scoped_refptr<base::MessageLoopProxy>& message_loop) {
116  DCHECK(io_message_loop_->BelongsToCurrentThread());
117  std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
118  for (; it != callbacks_.end(); ++it) {
119    if (it->first == id) {
120      // Callback is copied to heap and then deleted on the target thread.
121      scoped_ptr<VideoCaptureDeliverFrameCB> callback;
122      callback.reset(new VideoCaptureDeliverFrameCB(it->second));
123      callbacks_.erase(it);
124      message_loop->PostTask(
125          FROM_HERE, base::Bind(&ResetCallback, base::Passed(&callback)));
126      return;
127    }
128  }
129}
130
131void MediaStreamVideoTrack::FrameDeliverer::SetEnabled(bool enabled) {
132  DCHECK(thread_checker_.CalledOnValidThread());
133  io_message_loop_->PostTask(
134      FROM_HERE,
135      base::Bind(&FrameDeliverer::SetEnabledOnIO,
136                 this, enabled));
137}
138
139void MediaStreamVideoTrack::FrameDeliverer::SetEnabledOnIO(bool enabled) {
140  DCHECK(io_message_loop_->BelongsToCurrentThread());
141  enabled_ = enabled;
142  if (enabled_)
143    black_frame_ = NULL;
144}
145
146void MediaStreamVideoTrack::FrameDeliverer::DeliverFrameOnIO(
147    const scoped_refptr<media::VideoFrame>& frame,
148    const media::VideoCaptureFormat& format,
149    const base::TimeTicks& estimated_capture_time) {
150  DCHECK(io_message_loop_->BelongsToCurrentThread());
151  const scoped_refptr<media::VideoFrame>& video_frame =
152      enabled_ ? frame : GetBlackFrame(frame);
153
154  for (std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
155       it != callbacks_.end(); ++it) {
156    it->second.Run(video_frame, format, estimated_capture_time);
157  }
158}
159
160const scoped_refptr<media::VideoFrame>&
161MediaStreamVideoTrack::FrameDeliverer::GetBlackFrame(
162    const scoped_refptr<media::VideoFrame>& reference_frame) {
163  DCHECK(io_message_loop_->BelongsToCurrentThread());
164  if (!black_frame_.get() ||
165      black_frame_->natural_size() != reference_frame->natural_size())
166    black_frame_ =
167        media::VideoFrame::CreateBlackFrame(reference_frame->natural_size());
168
169  black_frame_->set_timestamp(reference_frame->timestamp());
170  return black_frame_;
171}
172
173// static
174blink::WebMediaStreamTrack MediaStreamVideoTrack::CreateVideoTrack(
175    MediaStreamVideoSource* source,
176    const blink::WebMediaConstraints& constraints,
177    const MediaStreamVideoSource::ConstraintsCallback& callback,
178    bool enabled) {
179  blink::WebMediaStreamTrack track;
180  track.initialize(source->owner());
181  track.setExtraData(new MediaStreamVideoTrack(source,
182                                               constraints,
183                                               callback,
184                                               enabled));
185  return track;
186}
187
188// static
189MediaStreamVideoTrack* MediaStreamVideoTrack::GetVideoTrack(
190     const blink::WebMediaStreamTrack& track) {
191  return static_cast<MediaStreamVideoTrack*>(track.extraData());
192}
193
194MediaStreamVideoTrack::MediaStreamVideoTrack(
195    MediaStreamVideoSource* source,
196    const blink::WebMediaConstraints& constraints,
197    const MediaStreamVideoSource::ConstraintsCallback& callback,
198    bool enabled)
199    : MediaStreamTrack(NULL, true),
200      frame_deliverer_(
201          new MediaStreamVideoTrack::FrameDeliverer(source->io_message_loop(),
202                                                    enabled)),
203      constraints_(constraints),
204      source_(source) {
205  DCHECK(!constraints.isNull());
206  source->AddTrack(this,
207                   base::Bind(
208                       &MediaStreamVideoTrack::FrameDeliverer::DeliverFrameOnIO,
209                       frame_deliverer_),
210                   constraints, callback);
211}
212
213MediaStreamVideoTrack::~MediaStreamVideoTrack() {
214  DCHECK(thread_checker_.CalledOnValidThread());
215  DCHECK(sinks_.empty());
216  Stop();
217  DVLOG(3) << "~MediaStreamVideoTrack()";
218}
219
220void MediaStreamVideoTrack::AddSink(
221    MediaStreamVideoSink* sink, const VideoCaptureDeliverFrameCB& callback) {
222  DCHECK(thread_checker_.CalledOnValidThread());
223  DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end());
224  sinks_.push_back(sink);
225  frame_deliverer_->AddCallback(sink, callback);
226}
227
228void MediaStreamVideoTrack::RemoveSink(MediaStreamVideoSink* sink) {
229  DCHECK(thread_checker_.CalledOnValidThread());
230  std::vector<MediaStreamVideoSink*>::iterator it =
231      std::find(sinks_.begin(), sinks_.end(), sink);
232  DCHECK(it != sinks_.end());
233  sinks_.erase(it);
234  frame_deliverer_->RemoveCallback(sink);
235}
236
237void MediaStreamVideoTrack::SetEnabled(bool enabled) {
238  DCHECK(thread_checker_.CalledOnValidThread());
239  MediaStreamTrack::SetEnabled(enabled);
240
241  frame_deliverer_->SetEnabled(enabled);
242  for (std::vector<MediaStreamVideoSink*>::const_iterator it = sinks_.begin();
243       it != sinks_.end(); ++it) {
244    (*it)->OnEnabledChanged(enabled);
245  }
246}
247
248void MediaStreamVideoTrack::Stop() {
249  DCHECK(thread_checker_.CalledOnValidThread());
250  if (source_) {
251    source_->RemoveTrack(this);
252    source_ = NULL;
253  }
254  OnReadyStateChanged(blink::WebMediaStreamSource::ReadyStateEnded);
255}
256
257void MediaStreamVideoTrack::OnReadyStateChanged(
258    blink::WebMediaStreamSource::ReadyState state) {
259  DCHECK(thread_checker_.CalledOnValidThread());
260  for (std::vector<MediaStreamVideoSink*>::const_iterator it = sinks_.begin();
261       it != sinks_.end(); ++it) {
262    (*it)->OnReadyStateChanged(state);
263  }
264}
265
266}  // namespace content
267