1// Copyright 2014 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/webrtc/webrtc_video_track_adapter.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "base/synchronization/lock.h"
9#include "content/common/media/media_stream_options.h"
10#include "content/renderer/media/media_stream_video_source.h"
11#include "content/renderer/media/media_stream_video_track.h"
12
13namespace {
14
15bool ConstraintKeyExists(const blink::WebMediaConstraints& constraints,
16                         const blink::WebString& name) {
17  blink::WebString value_str;
18  return constraints.getMandatoryConstraintValue(name, value_str) ||
19      constraints.getOptionalConstraintValue(name, value_str);
20}
21
22}  // anonymouse namespace
23
24namespace content {
25
26// Simple help class used for receiving video frames on the IO-thread from
27// a MediaStreamVideoTrack and forward the frames to a
28// WebRtcVideoCapturerAdapter on libjingle's worker thread.
29// WebRtcVideoCapturerAdapter implements a video capturer for libjingle.
30class WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter
31    : public base::RefCountedThreadSafe<WebRtcVideoSourceAdapter> {
32 public:
33  WebRtcVideoSourceAdapter(
34      const scoped_refptr<base::MessageLoopProxy>& libjingle_worker_thread,
35      const scoped_refptr<webrtc::VideoSourceInterface>& source,
36      WebRtcVideoCapturerAdapter* capture_adapter);
37
38  // WebRtcVideoTrackAdapter can be destroyed on the main render thread or
39  // libjingles worker thread since it posts video frames on that thread. But
40  // |video_source_| must be released on the main render thread before the
41  // PeerConnectionFactory has been destroyed. The only way to ensure that is
42  // to make sure |video_source_| is released when WebRtcVideoTrackAdapter() is
43  // destroyed.
44  void ReleaseSourceOnMainThread();
45
46  void OnVideoFrameOnIO(const scoped_refptr<media::VideoFrame>& frame,
47                        const media::VideoCaptureFormat& format,
48                        const base::TimeTicks& estimated_capture_time);
49
50 private:
51  void OnVideoFrameOnWorkerThread(
52      const scoped_refptr<media::VideoFrame>& frame,
53      const media::VideoCaptureFormat& format,
54      const base::TimeTicks& estimated_capture_time);
55  friend class base::RefCountedThreadSafe<WebRtcVideoSourceAdapter>;
56  virtual ~WebRtcVideoSourceAdapter();
57
58  scoped_refptr<base::MessageLoopProxy> render_thread_message_loop_;
59
60  // |render_thread_checker_| is bound to the main render thread.
61  base::ThreadChecker render_thread_checker_;
62  // Used to DCHECK that frames are called on the IO-thread.
63  base::ThreadChecker io_thread_checker_;
64
65  // Used for posting frames to libjingle's worker thread. Accessed on the
66  // IO-thread.
67  scoped_refptr<base::MessageLoopProxy> libjingle_worker_thread_;
68
69  scoped_refptr<webrtc::VideoSourceInterface> video_source_;
70
71  // Used to protect |capture_adapter_|. It is taken by libjingle's worker
72  // thread for each video frame that is delivered but only taken on the
73  // main render thread in ReleaseSourceOnMainThread() when
74  // the owning WebRtcVideoTrackAdapter is being destroyed.
75  base::Lock capture_adapter_stop_lock_;
76  // |capture_adapter_| is owned by |video_source_|
77  WebRtcVideoCapturerAdapter* capture_adapter_;
78};
79
80WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter::WebRtcVideoSourceAdapter(
81    const scoped_refptr<base::MessageLoopProxy>& libjingle_worker_thread,
82    const scoped_refptr<webrtc::VideoSourceInterface>& source,
83    WebRtcVideoCapturerAdapter* capture_adapter)
84    : render_thread_message_loop_(base::MessageLoopProxy::current()),
85      libjingle_worker_thread_(libjingle_worker_thread),
86      video_source_(source),
87      capture_adapter_(capture_adapter) {
88  io_thread_checker_.DetachFromThread();
89}
90
91WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter::~WebRtcVideoSourceAdapter() {
92  DVLOG(3) << "~WebRtcVideoSourceAdapter()";
93  DCHECK(!capture_adapter_);
94  // This object can be destroyed on the main render thread or libjingles
95  // worker thread since it posts video frames on that thread. But
96  // |video_source_| must be released on the main render thread before the
97  // PeerConnectionFactory has been destroyed. The only way to ensure that is
98  // to make sure |video_source_| is released when WebRtcVideoTrackAdapter() is
99  // destroyed.
100}
101
102void WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter::
103ReleaseSourceOnMainThread() {
104  DCHECK(render_thread_checker_.CalledOnValidThread());
105  // Since frames are posted to the worker thread, this object might be deleted
106  // on that thread. However, since |video_source_| was created on the render
107  // thread, it should be released on the render thread.
108  base::AutoLock auto_lock(capture_adapter_stop_lock_);
109  // |video_source| owns |capture_adapter_|.
110  capture_adapter_ = NULL;
111  video_source_ = NULL;
112}
113
114void WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter::OnVideoFrameOnIO(
115    const scoped_refptr<media::VideoFrame>& frame,
116    const media::VideoCaptureFormat& format,
117    const base::TimeTicks& estimated_capture_time) {
118  DCHECK(io_thread_checker_.CalledOnValidThread());
119  libjingle_worker_thread_->PostTask(
120      FROM_HERE,
121      base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnWorkerThread,
122                 this, frame, format, estimated_capture_time));
123}
124
125void
126WebRtcVideoTrackAdapter::WebRtcVideoSourceAdapter::OnVideoFrameOnWorkerThread(
127    const scoped_refptr<media::VideoFrame>& frame,
128    const media::VideoCaptureFormat& format,
129    const base::TimeTicks& estimated_capture_time) {
130  DCHECK(libjingle_worker_thread_->BelongsToCurrentThread());
131  base::AutoLock auto_lock(capture_adapter_stop_lock_);
132  if (capture_adapter_)
133    capture_adapter_->OnFrameCaptured(frame);
134}
135
136WebRtcVideoTrackAdapter::WebRtcVideoTrackAdapter(
137    const blink::WebMediaStreamTrack& track,
138    PeerConnectionDependencyFactory* factory)
139    : web_track_(track) {
140  const blink::WebMediaConstraints& constraints =
141      MediaStreamVideoTrack::GetVideoTrack(track)->constraints();
142
143  bool is_screencast = ConstraintKeyExists(
144      constraints, base::UTF8ToUTF16(kMediaStreamSource));
145  WebRtcVideoCapturerAdapter* capture_adapter =
146      factory->CreateVideoCapturer(is_screencast);
147
148  // |video_source| owns |capture_adapter|
149  scoped_refptr<webrtc::VideoSourceInterface> video_source(
150      factory->CreateVideoSource(capture_adapter,
151                                 track.source().constraints()));
152
153  video_track_ = factory->CreateLocalVideoTrack(web_track_.id().utf8(),
154                                                video_source.get());
155
156  video_track_->set_enabled(web_track_.isEnabled());
157
158  source_adapter_ = new WebRtcVideoSourceAdapter(
159      factory->GetWebRtcWorkerThread(),
160      video_source,
161      capture_adapter);
162
163  AddToVideoTrack(
164      this,
165      base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnIO,
166                 source_adapter_),
167      web_track_);
168
169  DVLOG(3) << "WebRtcVideoTrackAdapter ctor() : is_screencast "
170           << is_screencast;
171}
172
173WebRtcVideoTrackAdapter::~WebRtcVideoTrackAdapter() {
174  DCHECK(thread_checker_.CalledOnValidThread());
175  DVLOG(3) << "WebRtcVideoTrackAdapter dtor().";
176  RemoveFromVideoTrack(this, web_track_);
177  source_adapter_->ReleaseSourceOnMainThread();
178}
179
180void WebRtcVideoTrackAdapter::OnEnabledChanged(bool enabled) {
181  DCHECK(thread_checker_.CalledOnValidThread());
182  video_track_->set_enabled(enabled);
183}
184
185}  // namespace content
186