Home | History | Annotate | Download | only in media
      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 {
     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 
     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 
     85 MediaStreamVideoTrack::FrameDeliverer::~FrameDeliverer() {
     86   DCHECK(callbacks_.empty());
     87 }
     88 
     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 
     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 
    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 
    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 
    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 
    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 
    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>&
    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
    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
    189 MediaStreamVideoTrack* MediaStreamVideoTrack::GetVideoTrack(
    190      const blink::WebMediaStreamTrack& track) {
    191   return static_cast<MediaStreamVideoTrack*>(track.extraData());
    192 }
    193 
    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 
    213 MediaStreamVideoTrack::~MediaStreamVideoTrack() {
    214   DCHECK(thread_checker_.CalledOnValidThread());
    215   DCHECK(sinks_.empty());
    216   Stop();
    217   DVLOG(3) << "~MediaStreamVideoTrack()";
    218 }
    219 
    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 
    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 
    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 
    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 
    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