Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: content/renderer/media/webrtc/media_stream_video_webrtc_sink.cc

Issue 1849003002: Add video frame refresh to MediaStream and VideoCapture stacks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed nick's PS3 comments (moving non-observer impl out of MSVideoSink interface). Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/webrtc/media_stream_video_webrtc_sink.h" 5 #include "content/renderer/media/webrtc/media_stream_video_webrtc_sink.h"
6 6
7 #include "base/location.h" 7 #include "base/location.h"
8 #include "base/numerics/safe_conversions.h"
8 #include "base/single_thread_task_runner.h" 9 #include "base/single_thread_task_runner.h"
9 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
10 #include "base/synchronization/lock.h" 11 #include "base/synchronization/lock.h"
11 #include "base/thread_task_runner_handle.h" 12 #include "base/thread_task_runner_handle.h"
13 #include "base/timer/timer.h"
12 #include "content/common/media/media_stream_options.h" 14 #include "content/common/media/media_stream_options.h"
15 #include "content/public/renderer/media_stream_utils.h"
13 #include "content/renderer/media/media_stream_constraints_util.h" 16 #include "content/renderer/media/media_stream_constraints_util.h"
14 #include "content/renderer/media/media_stream_video_track.h" 17 #include "content/renderer/media/media_stream_video_track.h"
15 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" 18 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
19 #include "media/base/limits.h"
16 20
17 namespace content { 21 namespace content {
18 22
23 namespace {
24
25 // The default number of microseconds that should elapse since the last video
26 // frame was received, before requesting a refresh frame.
27 const int64_t kDefaultRefreshIntervalMicros =
28 base::Time::kMicrosecondsPerSecond;
29
30 // A lower-bound for the refresh interval.
31 const int64_t kLowerBoundRefreshIntervalMicros =
32 base::Time::kMicrosecondsPerSecond / media::limits::kMaxFramesPerSecond;
33
34 } // namespace
35
19 // Simple help class used for receiving video frames on the IO-thread from a 36 // Simple help class used for receiving video frames on the IO-thread from a
20 // MediaStreamVideoTrack and forward the frames to a WebRtcVideoCapturerAdapter 37 // MediaStreamVideoTrack and forward the frames to a WebRtcVideoCapturerAdapter
21 // on libjingle's worker thread. WebRtcVideoCapturerAdapter implements a video 38 // on libjingle's worker thread. WebRtcVideoCapturerAdapter implements a video
22 // capturer for libjingle. 39 // capturer for libjingle.
23 class MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter 40 class MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter
24 : public base::RefCountedThreadSafe<WebRtcVideoSourceAdapter> { 41 : public base::RefCountedThreadSafe<WebRtcVideoSourceAdapter> {
25 public: 42 public:
26 WebRtcVideoSourceAdapter( 43 WebRtcVideoSourceAdapter(
27 const scoped_refptr<base::SingleThreadTaskRunner>& 44 const scoped_refptr<base::SingleThreadTaskRunner>&
28 libjingle_worker_thread, 45 libjingle_worker_thread,
29 const scoped_refptr<webrtc::VideoTrackSourceInterface>& source, 46 const scoped_refptr<webrtc::VideoTrackSourceInterface>& source,
30 WebRtcVideoCapturerAdapter* capture_adapter); 47 WebRtcVideoCapturerAdapter* capture_adapter,
48 base::TimeDelta refresh_interval,
49 const base::Closure& refresh_callback);
31 50
32 // MediaStreamVideoWebRtcSink can be destroyed on the main render thread or 51 // MediaStreamVideoWebRtcSink can be destroyed on the main render thread or
33 // libjingles worker thread since it posts video frames on that thread. But 52 // libjingles worker thread since it posts video frames on that thread. But
34 // |video_source_| must be released on the main render thread before the 53 // |video_source_| must be released on the main render thread before the
35 // PeerConnectionFactory has been destroyed. The only way to ensure that is to 54 // PeerConnectionFactory has been destroyed. The only way to ensure that is to
36 // make sure |video_source_| is released when MediaStreamVideoWebRtcSink() is 55 // make sure |video_source_| is released when MediaStreamVideoWebRtcSink() is
37 // destroyed. 56 // destroyed.
38 void ReleaseSourceOnMainThread(); 57 void ReleaseSourceOnMainThread();
39 58
40 void OnVideoFrameOnIO(const scoped_refptr<media::VideoFrame>& frame, 59 void OnVideoFrameOnIO(const scoped_refptr<media::VideoFrame>& frame,
41 base::TimeTicks estimated_capture_time); 60 base::TimeTicks estimated_capture_time);
42 61
43 private: 62 private:
44 void OnVideoFrameOnWorkerThread( 63 void OnVideoFrameOnWorkerThread(
45 const scoped_refptr<media::VideoFrame>& frame); 64 const scoped_refptr<media::VideoFrame>& frame);
46 friend class base::RefCountedThreadSafe<WebRtcVideoSourceAdapter>; 65 friend class base::RefCountedThreadSafe<WebRtcVideoSourceAdapter>;
47 virtual ~WebRtcVideoSourceAdapter(); 66 virtual ~WebRtcVideoSourceAdapter();
48 67
68 // Called whenever a video frame was just delivered on the IO thread. This
69 // restarts the delay period before the |refresh_timer_| will fire the next
70 // time.
71 void ResetRefreshTimerOnMainThread();
72
49 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_; 73 scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
50 74
51 // |render_thread_checker_| is bound to the main render thread. 75 // |render_thread_checker_| is bound to the main render thread.
52 base::ThreadChecker render_thread_checker_; 76 base::ThreadChecker render_thread_checker_;
53 // Used to DCHECK that frames are called on the IO-thread. 77 // Used to DCHECK that frames are called on the IO-thread.
54 base::ThreadChecker io_thread_checker_; 78 base::ThreadChecker io_thread_checker_;
55 79
56 // Used for posting frames to libjingle's worker thread. Accessed on the 80 // Used for posting frames to libjingle's worker thread. Accessed on the
57 // IO-thread. 81 // IO-thread.
58 scoped_refptr<base::SingleThreadTaskRunner> libjingle_worker_thread_; 82 scoped_refptr<base::SingleThreadTaskRunner> libjingle_worker_thread_;
59 83
60 scoped_refptr<webrtc::VideoTrackSourceInterface> video_source_; 84 scoped_refptr<webrtc::VideoTrackSourceInterface> video_source_;
61 85
62 // Used to protect |capture_adapter_|. It is taken by libjingle's worker 86 // Used to protect |capture_adapter_|. It is taken by libjingle's worker
63 // thread for each video frame that is delivered but only taken on the 87 // thread for each video frame that is delivered but only taken on the
64 // main render thread in ReleaseSourceOnMainThread() when 88 // main render thread in ReleaseSourceOnMainThread() when
65 // the owning MediaStreamVideoWebRtcSink is being destroyed. 89 // the owning MediaStreamVideoWebRtcSink is being destroyed.
66 base::Lock capture_adapter_stop_lock_; 90 base::Lock capture_adapter_stop_lock_;
67 // |capture_adapter_| is owned by |video_source_| 91 // |capture_adapter_| is owned by |video_source_|
68 WebRtcVideoCapturerAdapter* capture_adapter_; 92 WebRtcVideoCapturerAdapter* capture_adapter_;
93
94 // Requests a refresh frame at regular intervals. The delay on this timer is
95 // reset each time a frame is received so that it will not fire for at least
96 // an additional period. This means refresh frames will only be requested when
97 // the source has halted delivery (e.g., a screen capturer stops sending
98 // frames because the screen is not being updated).
99 //
100 // This mechanism solves a number of problems. First, it will ensure that
101 // remote clients that join a distributed session receive a first video frame
102 // in a timely manner. Second, it will allow WebRTC's internal bandwidth
103 // estimation logic to maintain a more optimal state, since sending a video
104 // frame will "prime it." Third, it allows lossy encoders to clean up
105 // artifacts in a still image. http://crbug.com/486274
106 base::RepeatingTimer refresh_timer_;
69 }; 107 };
70 108
71 MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::WebRtcVideoSourceAdapter( 109 MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::WebRtcVideoSourceAdapter(
72 const scoped_refptr<base::SingleThreadTaskRunner>& libjingle_worker_thread, 110 const scoped_refptr<base::SingleThreadTaskRunner>& libjingle_worker_thread,
73 const scoped_refptr<webrtc::VideoTrackSourceInterface>& source, 111 const scoped_refptr<webrtc::VideoTrackSourceInterface>& source,
74 WebRtcVideoCapturerAdapter* capture_adapter) 112 WebRtcVideoCapturerAdapter* capture_adapter,
113 base::TimeDelta refresh_interval,
114 const base::Closure& refresh_callback)
75 : render_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), 115 : render_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
76 libjingle_worker_thread_(libjingle_worker_thread), 116 libjingle_worker_thread_(libjingle_worker_thread),
77 video_source_(source), 117 video_source_(source),
78 capture_adapter_(capture_adapter) { 118 capture_adapter_(capture_adapter) {
79 io_thread_checker_.DetachFromThread(); 119 io_thread_checker_.DetachFromThread();
120 if (!refresh_interval.is_zero()) {
121 VLOG(1) << "Starting frame refresh timer with interval "
122 << refresh_interval.InMillisecondsF() << " ms.";
123 refresh_timer_.Start(FROM_HERE, refresh_interval, refresh_callback);
124 }
80 } 125 }
81 126
82 MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter:: 127 MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::
83 ~WebRtcVideoSourceAdapter() { 128 ~WebRtcVideoSourceAdapter() {
84 DVLOG(3) << "~WebRtcVideoSourceAdapter()"; 129 DVLOG(3) << "~WebRtcVideoSourceAdapter()";
85 DCHECK(!capture_adapter_); 130 DCHECK(!capture_adapter_);
86 // This object can be destroyed on the main render thread or libjingles worker 131 // This object can be destroyed on the main render thread or libjingles worker
87 // thread since it posts video frames on that thread. But |video_source_| must 132 // thread since it posts video frames on that thread. But |video_source_| must
88 // be released on the main render thread before the PeerConnectionFactory has 133 // be released on the main render thread before the PeerConnectionFactory has
89 // been destroyed. The only way to ensure that is to make sure |video_source_| 134 // been destroyed. The only way to ensure that is to make sure |video_source_|
90 // is released when MediaStreamVideoWebRtcSink() is destroyed. 135 // is released when MediaStreamVideoWebRtcSink() is destroyed.
91 } 136 }
92 137
93 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter:: 138 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::
139 ResetRefreshTimerOnMainThread() {
140 DCHECK(render_thread_checker_.CalledOnValidThread());
141 if (refresh_timer_.IsRunning())
142 refresh_timer_.Reset();
143 }
144
145 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::
94 ReleaseSourceOnMainThread() { 146 ReleaseSourceOnMainThread() {
95 DCHECK(render_thread_checker_.CalledOnValidThread()); 147 DCHECK(render_thread_checker_.CalledOnValidThread());
96 // Since frames are posted to the worker thread, this object might be deleted 148 // Since frames are posted to the worker thread, this object might be deleted
97 // on that thread. However, since |video_source_| was created on the render 149 // on that thread. However, since |video_source_| was created on the render
98 // thread, it should be released on the render thread. 150 // thread, it should be released on the render thread.
99 base::AutoLock auto_lock(capture_adapter_stop_lock_); 151 base::AutoLock auto_lock(capture_adapter_stop_lock_);
100 // |video_source| owns |capture_adapter_|. 152 // |video_source| owns |capture_adapter_|.
101 capture_adapter_ = NULL; 153 capture_adapter_ = NULL;
102 video_source_ = NULL; 154 video_source_ = NULL;
103 } 155 }
104 156
105 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::OnVideoFrameOnIO( 157 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::OnVideoFrameOnIO(
106 const scoped_refptr<media::VideoFrame>& frame, 158 const scoped_refptr<media::VideoFrame>& frame,
107 base::TimeTicks estimated_capture_time) { 159 base::TimeTicks estimated_capture_time) {
108 DCHECK(io_thread_checker_.CalledOnValidThread()); 160 DCHECK(io_thread_checker_.CalledOnValidThread());
161 render_thread_task_runner_->PostTask(
162 FROM_HERE,
163 base::Bind(&WebRtcVideoSourceAdapter::ResetRefreshTimerOnMainThread,
164 this));
109 libjingle_worker_thread_->PostTask( 165 libjingle_worker_thread_->PostTask(
110 FROM_HERE, 166 FROM_HERE,
111 base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnWorkerThread, 167 base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnWorkerThread,
112 this, 168 this,
113 frame)); 169 frame));
114 } 170 }
115 171
116 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter:: 172 void MediaStreamVideoWebRtcSink::WebRtcVideoSourceAdapter::
117 OnVideoFrameOnWorkerThread(const scoped_refptr<media::VideoFrame>& frame) { 173 OnVideoFrameOnWorkerThread(const scoped_refptr<media::VideoFrame>& frame) {
118 DCHECK(libjingle_worker_thread_->BelongsToCurrentThread()); 174 DCHECK(libjingle_worker_thread_->BelongsToCurrentThread());
119 base::AutoLock auto_lock(capture_adapter_stop_lock_); 175 base::AutoLock auto_lock(capture_adapter_stop_lock_);
120 if (capture_adapter_) 176 if (capture_adapter_)
121 capture_adapter_->OnFrameCaptured(frame); 177 capture_adapter_->OnFrameCaptured(frame);
122 } 178 }
123 179
124 MediaStreamVideoWebRtcSink::MediaStreamVideoWebRtcSink( 180 MediaStreamVideoWebRtcSink::MediaStreamVideoWebRtcSink(
125 const blink::WebMediaStreamTrack& track, 181 const blink::WebMediaStreamTrack& track,
126 PeerConnectionDependencyFactory* factory) 182 PeerConnectionDependencyFactory* factory)
127 : web_track_(track) { 183 : weak_factory_(this) {
128 const blink::WebMediaConstraints& constraints = 184 const blink::WebMediaConstraints& constraints =
129 MediaStreamVideoTrack::GetVideoTrack(track)->constraints(); 185 MediaStreamVideoTrack::GetVideoTrack(track)->constraints();
130 186
131 // Check for presence of mediaStreamSource constraint. The value is ignored. 187 // Check for presence of mediaStreamSource constraint. The value is ignored.
132 std::string value; 188 std::string value;
133 bool is_screencast = GetConstraintValueAsString( 189 bool is_screencast = GetConstraintValueAsString(
134 constraints, &blink::WebMediaTrackConstraintSet::mediaStreamSource, 190 constraints, &blink::WebMediaTrackConstraintSet::mediaStreamSource,
135 &value); 191 &value);
192
193 // Enable automatic frame refreshes for the screen capture sources, which will
194 // stop producing frames whenever screen content is not changing. Check the
195 // frameRate constraint to determine the rate of refreshes. If a minimum
196 // frameRate is provided, use that. Otherwise, use the maximum frameRate if it
197 // happens to be less than the default.
198 base::TimeDelta refresh_interval = base::TimeDelta::FromMicroseconds(0);
199 if (is_screencast) {
200 // Start with the default refresh interval, and refine based on constraints.
201 refresh_interval =
202 base::TimeDelta::FromMicroseconds(kDefaultRefreshIntervalMicros);
203 double value = 0.0;
204 if (GetConstraintMinAsDouble(
205 constraints, &blink::WebMediaTrackConstraintSet::frameRate,
206 &value) &&
207 value > 0.0) {
208 refresh_interval =
209 base::TimeDelta::FromMicroseconds(base::saturated_cast<int64_t>(
210 base::Time::kMicrosecondsPerSecond / value));
211 }
212 if (GetConstraintMaxAsDouble(
213 constraints, &blink::WebMediaTrackConstraintSet::frameRate,
214 &value) &&
215 value > 0.0) {
216 const base::TimeDelta alternate_refresh_interval =
217 base::TimeDelta::FromMicroseconds(base::saturated_cast<int64_t>(
218 base::Time::kMicrosecondsPerSecond / value));
219 refresh_interval = std::max(refresh_interval, alternate_refresh_interval);
220 }
221 if (refresh_interval.InMicroseconds() < kLowerBoundRefreshIntervalMicros) {
222 refresh_interval =
223 base::TimeDelta::FromMicroseconds(kLowerBoundRefreshIntervalMicros);
224 }
225 }
226
136 WebRtcVideoCapturerAdapter* capture_adapter = 227 WebRtcVideoCapturerAdapter* capture_adapter =
137 factory->CreateVideoCapturer(is_screencast); 228 factory->CreateVideoCapturer(is_screencast);
138 229
139 // |video_source| owns |capture_adapter| 230 // |video_source| owns |capture_adapter|
140 scoped_refptr<webrtc::VideoTrackSourceInterface> video_source( 231 scoped_refptr<webrtc::VideoTrackSourceInterface> video_source(
141 factory->CreateVideoSource(capture_adapter)); 232 factory->CreateVideoSource(capture_adapter));
142 233
143 video_track_ = factory->CreateLocalVideoTrack(web_track_.id().utf8(), 234 video_track_ = factory->CreateLocalVideoTrack(track.id().utf8(),
144 video_source.get()); 235 video_source.get());
145 236
146 video_track_->set_enabled(web_track_.isEnabled()); 237 video_track_->set_enabled(track.isEnabled());
147 238
148 source_adapter_ = new WebRtcVideoSourceAdapter( 239 source_adapter_ = new WebRtcVideoSourceAdapter(
149 factory->GetWebRtcWorkerThread(), 240 factory->GetWebRtcWorkerThread(),
150 video_source, 241 video_source,
151 capture_adapter); 242 capture_adapter,
243 refresh_interval,
244 base::Bind(&MediaStreamVideoWebRtcSink::RequestRefreshFrame,
245 weak_factory_.GetWeakPtr()));
152 246
153 AddToVideoTrack( 247 MediaStreamVideoSink::ConnectToTrack(
154 this, 248 track,
155 base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnIO, source_adapter_), 249 base::Bind(&WebRtcVideoSourceAdapter::OnVideoFrameOnIO, source_adapter_));
156 web_track_);
157 250
158 DVLOG(3) << "MediaStreamVideoWebRtcSink ctor() : is_screencast " 251 DVLOG(3) << "MediaStreamVideoWebRtcSink ctor() : is_screencast "
159 << is_screencast; 252 << is_screencast;
160 } 253 }
161 254
162 MediaStreamVideoWebRtcSink::~MediaStreamVideoWebRtcSink() { 255 MediaStreamVideoWebRtcSink::~MediaStreamVideoWebRtcSink() {
163 DCHECK(thread_checker_.CalledOnValidThread()); 256 DCHECK(thread_checker_.CalledOnValidThread());
164 DVLOG(3) << "MediaStreamVideoWebRtcSink dtor()."; 257 DVLOG(3) << "MediaStreamVideoWebRtcSink dtor().";
165 RemoveFromVideoTrack(this, web_track_); 258 weak_factory_.InvalidateWeakPtrs();
259 MediaStreamVideoSink::DisconnectFromTrack();
166 source_adapter_->ReleaseSourceOnMainThread(); 260 source_adapter_->ReleaseSourceOnMainThread();
167 } 261 }
168 262
169 void MediaStreamVideoWebRtcSink::OnEnabledChanged(bool enabled) { 263 void MediaStreamVideoWebRtcSink::OnEnabledChanged(bool enabled) {
170 DCHECK(thread_checker_.CalledOnValidThread()); 264 DCHECK(thread_checker_.CalledOnValidThread());
171 video_track_->set_enabled(enabled); 265 video_track_->set_enabled(enabled);
172 } 266 }
173 267
268 void MediaStreamVideoWebRtcSink::RequestRefreshFrame() {
269 DCHECK(thread_checker_.CalledOnValidThread());
270 content::RequestRefreshFrameFromVideoTrack(connected_track());
271 }
272
174 } // namespace content 273 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698