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