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
12 namespace content {
13
14 namespace {
ResetCallback(scoped_ptr<VideoCaptureDeliverFrameCB> callback)15 void 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.
26 class 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
FrameDeliverer(const scoped_refptr<base::MessageLoopProxy> & io_message_loop,bool enabled)78 MediaStreamVideoTrack::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
~FrameDeliverer()85 MediaStreamVideoTrack::FrameDeliverer::~FrameDeliverer() {
86 DCHECK(callbacks_.empty());
87 }
88
AddCallback(void * id,const VideoCaptureDeliverFrameCB & callback)89 void 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
AddCallbackOnIO(void * id,const VideoCaptureDeliverFrameCB & callback)99 void 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
RemoveCallback(void * id)106 void 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
RemoveCallbackOnIO(void * id,const scoped_refptr<base::MessageLoopProxy> & message_loop)114 void 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
SetEnabled(bool enabled)131 void 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
SetEnabledOnIO(bool enabled)139 void MediaStreamVideoTrack::FrameDeliverer::SetEnabledOnIO(bool enabled) {
140 DCHECK(io_message_loop_->BelongsToCurrentThread());
141 enabled_ = enabled;
142 if (enabled_)
143 black_frame_ = NULL;
144 }
145
DeliverFrameOnIO(const scoped_refptr<media::VideoFrame> & frame,const media::VideoCaptureFormat & format,const base::TimeTicks & estimated_capture_time)146 void 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
160 const scoped_refptr<media::VideoFrame>&
GetBlackFrame(const scoped_refptr<media::VideoFrame> & reference_frame)161 MediaStreamVideoTrack::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
CreateVideoTrack(MediaStreamVideoSource * source,const blink::WebMediaConstraints & constraints,const MediaStreamVideoSource::ConstraintsCallback & callback,bool enabled)174 blink::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
GetVideoTrack(const blink::WebMediaStreamTrack & track)189 MediaStreamVideoTrack* MediaStreamVideoTrack::GetVideoTrack(
190 const blink::WebMediaStreamTrack& track) {
191 return static_cast<MediaStreamVideoTrack*>(track.extraData());
192 }
193
MediaStreamVideoTrack(MediaStreamVideoSource * source,const blink::WebMediaConstraints & constraints,const MediaStreamVideoSource::ConstraintsCallback & callback,bool enabled)194 MediaStreamVideoTrack::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
~MediaStreamVideoTrack()213 MediaStreamVideoTrack::~MediaStreamVideoTrack() {
214 DCHECK(thread_checker_.CalledOnValidThread());
215 DCHECK(sinks_.empty());
216 Stop();
217 DVLOG(3) << "~MediaStreamVideoTrack()";
218 }
219
AddSink(MediaStreamVideoSink * sink,const VideoCaptureDeliverFrameCB & callback)220 void 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
RemoveSink(MediaStreamVideoSink * sink)228 void 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
SetEnabled(bool enabled)237 void 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
Stop()248 void 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
OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state)257 void 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