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

Side by Side Diff: content/renderer/media/media_stream_video_renderer_sink.cc

Issue 2472273002: Move passing of WebRTC rendering frames from main thread to compositor thread (Closed)
Patch Set: dalecurtis@ comments. Created 4 years, 1 month 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 (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/media_stream_video_renderer_sink.h" 5 #include "content/renderer/media/media_stream_video_renderer_sink.h"
6 6
7 #include "base/feature_list.h" 7 #include "base/feature_list.h"
8 #include "base/memory/weak_ptr.h"
8 #include "base/single_thread_task_runner.h" 9 #include "base/single_thread_task_runner.h"
9 #include "base/threading/thread_task_runner_handle.h" 10 #include "base/threading/thread_task_runner_handle.h"
10 #include "base/trace_event/trace_event.h" 11 #include "base/trace_event/trace_event.h"
12 #include "content/child/child_process.h"
11 #include "content/public/common/content_features.h" 13 #include "content/public/common/content_features.h"
14 #include "content/public/renderer/render_thread.h"
12 #include "media/base/bind_to_current_loop.h" 15 #include "media/base/bind_to_current_loop.h"
13 #include "media/base/video_frame.h" 16 #include "media/base/video_frame.h"
14 #include "media/base/video_frame_metadata.h" 17 #include "media/base/video_frame_metadata.h"
15 #include "media/base/video_util.h" 18 #include "media/base/video_util.h"
16 #include "media/renderers/gpu_video_accelerator_factories.h" 19 #include "media/renderers/gpu_video_accelerator_factories.h"
20 #include "media/video/gpu_memory_buffer_video_frame_pool.h"
17 21
18 const int kMinFrameSize = 2; 22 const int kMinFrameSize = 2;
19 23
20 namespace content { 24 namespace content {
21 25
26 // FrameDeliverer is responsible for delivering frames received on
27 // OnVideoFrame() to |repaint_cb_| on compositor thread.
28 //
29 // It is created on main thread, but methods should be called and class should
30 // be destructed on compositor thread.
perkj_chrome 2016/11/17 21:54:01 /s on the compositor thread
emircan 2016/11/17 23:03:21 Done.
31 class MediaStreamVideoRendererSink::FrameDeliverer {
32 public:
33 FrameDeliverer(const RepaintCB& repaint_cb,
34 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
35 scoped_refptr<base::TaskRunner> worker_task_runner,
36 media::GpuVideoAcceleratorFactories* gpu_factories)
37 : repaint_cb_(repaint_cb),
38 state_(STOPPED),
39 frame_size_(kMinFrameSize, kMinFrameSize),
40 media_task_runner_(media_task_runner),
41 weak_factory_for_compositor_(this) {
42 compositor_thread_checker_.DetachFromThread();
43 weak_this_ = weak_factory_for_compositor_.GetWeakPtr();
44 if (gpu_factories &&
45 gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames() &&
46 base::FeatureList::IsEnabled(
47 features::kWebRtcUseGpuMemoryBufferVideoFrames)) {
48 gpu_memory_buffer_pool_.reset(new media::GpuMemoryBufferVideoFramePool(
49 media_task_runner, worker_task_runner, gpu_factories));
50 }
51 }
52
53 ~FrameDeliverer() {
54 DCHECK(compositor_thread_checker_.CalledOnValidThread());
55 if (gpu_memory_buffer_pool_) {
56 media_task_runner_->DeleteSoon(FROM_HERE,
57 gpu_memory_buffer_pool_.release());
58 }
59 }
60
61 void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
62 base::TimeTicks /*current_time*/) {
63 DCHECK(compositor_thread_checker_.CalledOnValidThread());
64 DCHECK(frame);
65 TRACE_EVENT_INSTANT1("webrtc",
66 "MediaStreamVideoRendererSink::"
67 "FrameDeliverer::OnVideoFrame",
68 TRACE_EVENT_SCOPE_THREAD, "timestamp",
69 frame->timestamp().InMilliseconds());
70
71 if (state_ != STARTED)
72 return;
73
74 if (!gpu_memory_buffer_pool_) {
75 FrameReady(frame);
76 return;
77 }
78
79 // |gpu_memory_buffer_pool_| deletion is going to be posted to
80 // |media_task_runner_|. base::Unretained() usage is fine since
81 // |gpu_memory_buffer_pool_| outlives the task.
82 media_task_runner_->PostTask(
83 FROM_HERE,
84 base::Bind(
85 &media::GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
86 base::Unretained(gpu_memory_buffer_pool_.get()), frame,
87 media::BindToCurrentLoop(
88 base::Bind(&FrameDeliverer::FrameReady, weak_this_))));
89 }
90
91 void FrameReady(const scoped_refptr<media::VideoFrame>& frame) {
92 DCHECK(compositor_thread_checker_.CalledOnValidThread());
93 DCHECK(frame);
94 TRACE_EVENT_INSTANT1(
95 "webrtc", "MediaStreamVideoRendererSink::FrameDeliverer::FrameReady",
96 TRACE_EVENT_SCOPE_THREAD, "timestamp",
97 frame->timestamp().InMilliseconds());
98
99 frame_size_ = frame->natural_size();
100 repaint_cb_.Run(frame);
101 }
102
103 void RenderEndOfStream() {
104 DCHECK(compositor_thread_checker_.CalledOnValidThread());
105 // This is necessary to make sure audio can play if the video tag src is a
106 // MediaStream video track that has been rejected or ended. It also ensure
107 // that the renderer doesn't hold a reference to a real video frame if no
108 // more frames are provided. This is since there might be a finite number
109 // of available buffers. E.g, video that originates from a video camera.
110 scoped_refptr<media::VideoFrame> video_frame =
111 media::VideoFrame::CreateBlackFrame(
112 state_ == STOPPED ? gfx::Size(kMinFrameSize, kMinFrameSize)
113 : frame_size_);
114 video_frame->metadata()->SetBoolean(
115 media::VideoFrameMetadata::END_OF_STREAM, true);
116 video_frame->metadata()->SetTimeTicks(
117 media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks::Now());
118 OnVideoFrame(video_frame, base::TimeTicks());
119 }
120
121 void Start() {
122 DCHECK(compositor_thread_checker_.CalledOnValidThread());
123 DCHECK_EQ(state_, STOPPED);
124 state_ = STARTED;
125 }
126
127 void Stop() {
128 DCHECK(compositor_thread_checker_.CalledOnValidThread());
129 DCHECK(state_ == STARTED || state_ == PAUSED) << state_;
130 state_ = STOPPED;
131 weak_factory_for_compositor_.InvalidateWeakPtrs();
Wez 2016/11/18 02:26:56 Is there any point InvalidateWeakPtrs()ing here, s
emircan 2016/11/18 21:14:18 Sure, I moved it into the dtor.
132 }
133
134 void Resume() {
135 DCHECK(compositor_thread_checker_.CalledOnValidThread());
136 if (state_ == PAUSED)
137 state_ = STARTED;
138 }
139
140 void Pause() {
141 DCHECK(compositor_thread_checker_.CalledOnValidThread());
142 if (state_ == STARTED)
143 state_ = PAUSED;
144 }
145
146 VideoCaptureDeliverFrameCB GetDeliverFrameCallback() {
147 return base::Bind(&FrameDeliverer::OnVideoFrame, weak_this_);
148 }
149
150 private:
151 friend class MediaStreamVideoRendererSink;
152
153 void SetGpuMemoryBufferVideoForTesting(
154 media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) {
155 DCHECK(compositor_thread_checker_.CalledOnValidThread());
156 gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool);
157 }
158
159 const RepaintCB repaint_cb_;
160 State state_;
161 gfx::Size frame_size_;
162
163 // Pool of GpuMemoryBuffers and resources used to create hardware frames.
164 std::unique_ptr<media::GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool_;
165 const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
166
167 // Used for DCHECKs to ensure method calls are executed on the correct thread.
168 base::ThreadChecker compositor_thread_checker_;
169
170 base::WeakPtr<FrameDeliverer> weak_this_;
Wez 2016/11/18 02:26:56 nit: FYI it is strictly safest to put |weak_ptr_|
emircan 2016/11/18 21:14:18 Thanks, it is a very helpful tip.
171 base::WeakPtrFactory<FrameDeliverer> weak_factory_for_compositor_;
Wez 2016/11/18 02:26:56 This can just be |weak_factory_|, since this class
emircan 2016/11/18 21:14:18 Done.
172
173 DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
174 };
175
176 // FrameReceiver is responsible for trampolining frames from IO thread to
177 // the compositor thread by posting a task on |deliver_frame_cb_|.
178 //
179 // It is created on main thread, but methods should be called and class should
180 // be destructed on the IO thread.
181 class MediaStreamVideoRendererSink::FrameReceiver {
182 public:
183 FrameReceiver(
184 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
185 const VideoCaptureDeliverFrameCB& deliver_frame_cb)
186 : compositor_task_runner_(compositor_task_runner),
187 deliver_frame_cb_(deliver_frame_cb) {
188 io_thread_checker_.DetachFromThread();
189 }
190
191 ~FrameReceiver() { DCHECK(io_thread_checker_.CalledOnValidThread()); }
192
193 void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
194 base::TimeTicks current_time) {
195 DCHECK(io_thread_checker_.CalledOnValidThread());
196 compositor_task_runner_->PostTask(
197 FROM_HERE, base::Bind(deliver_frame_cb_, frame, current_time));
198 }
199
200 private:
201 const scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
202 const VideoCaptureDeliverFrameCB deliver_frame_cb_;
203
204 // Used for DCHECKs to ensure method calls executed in the correct thread.
205 base::ThreadChecker io_thread_checker_;
206
207 DISALLOW_COPY_AND_ASSIGN(FrameReceiver);
208 };
209
22 MediaStreamVideoRendererSink::MediaStreamVideoRendererSink( 210 MediaStreamVideoRendererSink::MediaStreamVideoRendererSink(
23 const blink::WebMediaStreamTrack& video_track, 211 const blink::WebMediaStreamTrack& video_track,
24 const base::Closure& error_cb, 212 const base::Closure& error_cb,
25 const RepaintCB& repaint_cb, 213 const RepaintCB& repaint_cb,
214 const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
26 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, 215 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
27 const scoped_refptr<base::TaskRunner>& worker_task_runner, 216 const scoped_refptr<base::TaskRunner>& worker_task_runner,
28 media::GpuVideoAcceleratorFactories* gpu_factories) 217 media::GpuVideoAcceleratorFactories* gpu_factories)
29 : error_cb_(error_cb), 218 : error_cb_(error_cb),
30 repaint_cb_(repaint_cb), 219 repaint_cb_(repaint_cb),
31 task_runner_(base::ThreadTaskRunnerHandle::Get()),
32 state_(STOPPED),
33 frame_size_(kMinFrameSize, kMinFrameSize),
34 video_track_(video_track), 220 video_track_(video_track),
221 compositor_task_runner_(compositor_task_runner),
35 media_task_runner_(media_task_runner), 222 media_task_runner_(media_task_runner),
36 weak_factory_(this) { 223 worker_task_runner_(worker_task_runner),
37 if (gpu_factories && 224 gpu_factories_(gpu_factories) {}
38 gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames() && 225
39 base::FeatureList::IsEnabled( 226 MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() {}
40 features::kWebRtcUseGpuMemoryBufferVideoFrames)) {
41 gpu_memory_buffer_pool_.reset(new media::GpuMemoryBufferVideoFramePool(
42 media_task_runner, worker_task_runner, gpu_factories));
43 }
44 }
45
46 MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() {
47 if (gpu_memory_buffer_pool_) {
48 media_task_runner_->DeleteSoon(FROM_HERE,
49 gpu_memory_buffer_pool_.release());
50 }
51 }
52 227
53 void MediaStreamVideoRendererSink::Start() { 228 void MediaStreamVideoRendererSink::Start() {
54 DCHECK(task_runner_->BelongsToCurrentThread()); 229 DCHECK(main_thread_checker_.CalledOnValidThread());
55 DCHECK_EQ(state_, STOPPED); 230
56 231 frame_deliverer_.reset(new MediaStreamVideoRendererSink::FrameDeliverer(
232 repaint_cb_, media_task_runner_, worker_task_runner_, gpu_factories_));
233 compositor_task_runner_->PostTask(
234 FROM_HERE, base::Bind(&FrameDeliverer::Start,
235 base::Unretained(frame_deliverer_.get())));
236
237 frame_receiver_.reset(new MediaStreamVideoRendererSink::FrameReceiver(
238 compositor_task_runner_, frame_deliverer_->GetDeliverFrameCallback()));
57 MediaStreamVideoSink::ConnectToTrack( 239 MediaStreamVideoSink::ConnectToTrack(
58 video_track_, media::BindToCurrentLoop( 240 video_track_,
59 base::Bind(&MediaStreamVideoRendererSink::OnVideoFrame, 241 // This callback is run on IO thread. It is safe to use base::Unretained
60 weak_factory_.GetWeakPtr())), 242 // here because |frame_receiver_| will be destroyed after sink is
243 // disconnected from track.
244 base::Bind(&FrameReceiver::OnVideoFrame,
245 base::Unretained(frame_receiver_.get())),
61 // Local display video rendering is considered a secure link. 246 // Local display video rendering is considered a secure link.
62 true); 247 true);
63 state_ = STARTED;
64 248
65 if (video_track_.source().getReadyState() == 249 if (video_track_.source().getReadyState() ==
66 blink::WebMediaStreamSource::ReadyStateEnded || 250 blink::WebMediaStreamSource::ReadyStateEnded ||
67 !video_track_.isEnabled()) { 251 !video_track_.isEnabled()) {
68 RenderSignalingFrame(); 252 compositor_task_runner_->PostTask(
253 FROM_HERE, base::Bind(&FrameDeliverer::RenderEndOfStream,
254 base::Unretained(frame_deliverer_.get())));
69 } 255 }
70 } 256 }
71 257
72 void MediaStreamVideoRendererSink::Stop() { 258 void MediaStreamVideoRendererSink::Stop() {
73 DCHECK(task_runner_->BelongsToCurrentThread()); 259 DCHECK(main_thread_checker_.CalledOnValidThread());
74 DCHECK(state_ == STARTED || state_ == PAUSED); 260
75 MediaStreamVideoSink::DisconnectFromTrack(); 261 MediaStreamVideoSink::DisconnectFromTrack();
76 weak_factory_.InvalidateWeakPtrs(); 262 if (frame_receiver_) {
77 state_ = STOPPED; 263 GetIOTaskRunner()->DeleteSoon(FROM_HERE, frame_receiver_.release());
perkj_chrome 2016/11/17 21:54:01 Is Stop always called? Othertwise frame_receiver w
emircan 2016/11/17 23:03:21 Yes, see https://cs.chromium.org/chromium/src/cont
78 frame_size_.set_width(kMinFrameSize); 264 }
79 frame_size_.set_height(kMinFrameSize); 265 if (frame_deliverer_) {
266 compositor_task_runner_->PostTask(
267 FROM_HERE, base::Bind(&FrameDeliverer::Stop,
268 base::Unretained(frame_deliverer_.get())));
269 compositor_task_runner_->DeleteSoon(FROM_HERE, frame_deliverer_.release());
270 }
80 } 271 }
81 272
82 void MediaStreamVideoRendererSink::Resume() { 273 void MediaStreamVideoRendererSink::Resume() {
83 DCHECK(task_runner_->BelongsToCurrentThread()); 274 DCHECK(main_thread_checker_.CalledOnValidThread());
84 if (state_ == PAUSED) 275 if (!frame_deliverer_)
85 state_ = STARTED; 276 return;
277
278 compositor_task_runner_->PostTask(
279 FROM_HERE, base::Bind(&FrameDeliverer::Resume,
280 base::Unretained(frame_deliverer_.get())));
86 } 281 }
87 282
88 void MediaStreamVideoRendererSink::Pause() { 283 void MediaStreamVideoRendererSink::Pause() {
89 DCHECK(task_runner_->BelongsToCurrentThread()); 284 DCHECK(main_thread_checker_.CalledOnValidThread());
90 if (state_ == STARTED) 285 if (!frame_deliverer_)
91 state_ = PAUSED; 286 return;
287
288 compositor_task_runner_->PostTask(
289 FROM_HERE, base::Bind(&FrameDeliverer::Pause,
290 base::Unretained(frame_deliverer_.get())));
291 }
292
293 void MediaStreamVideoRendererSink::OnReadyStateChanged(
294 blink::WebMediaStreamSource::ReadyState state) {
295 DCHECK(main_thread_checker_.CalledOnValidThread());
296 if (state == blink::WebMediaStreamSource::ReadyStateEnded &&
297 frame_deliverer_) {
298 compositor_task_runner_->PostTask(
299 FROM_HERE, base::Bind(&FrameDeliverer::RenderEndOfStream,
300 base::Unretained(frame_deliverer_.get())));
301 }
302 }
303
304 scoped_refptr<base::SingleThreadTaskRunner>
305 MediaStreamVideoRendererSink::GetIOTaskRunner() {
306 return ChildProcess::current()->io_task_runner();
307 }
308
309 MediaStreamVideoRendererSink::State
310 MediaStreamVideoRendererSink::GetStateForTesting() {
311 DCHECK(main_thread_checker_.CalledOnValidThread());
312 if (!frame_deliverer_)
313 return STOPPED;
314 return frame_deliverer_->state_;
92 } 315 }
93 316
94 void MediaStreamVideoRendererSink::SetGpuMemoryBufferVideoForTesting( 317 void MediaStreamVideoRendererSink::SetGpuMemoryBufferVideoForTesting(
95 media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) { 318 media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) {
96 gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool); 319 DCHECK(main_thread_checker_.CalledOnValidThread());
97 } 320 CHECK(frame_deliverer_);
98 321 compositor_task_runner_->PostTask(
99 void MediaStreamVideoRendererSink::OnReadyStateChanged( 322 FROM_HERE, base::Bind(&FrameDeliverer::SetGpuMemoryBufferVideoForTesting,
100 blink::WebMediaStreamSource::ReadyState state) { 323 base::Unretained(frame_deliverer_.get()),
101 DCHECK(task_runner_->BelongsToCurrentThread()); 324 gpu_memory_buffer_pool));
102 if (state == blink::WebMediaStreamSource::ReadyStateEnded)
103 RenderSignalingFrame();
104 }
105
106 void MediaStreamVideoRendererSink::OnVideoFrame(
107 const scoped_refptr<media::VideoFrame>& frame,
108 base::TimeTicks estimated_capture_time) {
109 DCHECK(task_runner_->BelongsToCurrentThread());
110 DCHECK(frame);
111 if (state_ != STARTED)
112 return;
113
114 if (!gpu_memory_buffer_pool_) {
115 FrameReady(frame);
116 return;
117 }
118
119 // |gpu_memory_buffer_pool_| deletion is going to be posted to
120 // |media_task_runner_|. base::Unretained() usage is fine since
121 // |gpu_memory_buffer_pool_| outlives the task.
122 media_task_runner_->PostTask(
123 FROM_HERE,
124 base::Bind(
125 &media::GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
126 base::Unretained(gpu_memory_buffer_pool_.get()), frame,
127 media::BindToCurrentLoop(
128 base::Bind(&MediaStreamVideoRendererSink::FrameReady,
129 weak_factory_.GetWeakPtr()))));
130 }
131
132 void MediaStreamVideoRendererSink::FrameReady(
133 const scoped_refptr<media::VideoFrame>& frame) {
134 DCHECK(task_runner_->BelongsToCurrentThread());
135 DCHECK(frame);
136
137 frame_size_ = frame->natural_size();
138 TRACE_EVENT_INSTANT1("media_stream_video_renderer_sink", "FrameReady",
139 TRACE_EVENT_SCOPE_THREAD, "timestamp",
140 frame->timestamp().InMilliseconds());
141 repaint_cb_.Run(frame);
142 }
143
144 void MediaStreamVideoRendererSink::RenderSignalingFrame() {
145 // This is necessary to make sure audio can play if the video tag src is
146 // a MediaStream video track that has been rejected or ended.
147 // It also ensure that the renderer don't hold a reference to a real video
148 // frame if no more frames are provided. This is since there might be a
149 // finite number of available buffers. E.g, video that
150 // originates from a video camera.
151 scoped_refptr<media::VideoFrame> video_frame =
152 media::VideoFrame::CreateBlackFrame(frame_size_);
153 video_frame->metadata()->SetBoolean(media::VideoFrameMetadata::END_OF_STREAM,
154 true);
155 video_frame->metadata()->SetTimeTicks(
156 media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks::Now());
157 OnVideoFrame(video_frame, base::TimeTicks());
158 } 325 }
159 326
160 } // namespace content 327 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698