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

Unified 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: wez@ nits. 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 side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/media_stream_video_renderer_sink.cc
diff --git a/content/renderer/media/media_stream_video_renderer_sink.cc b/content/renderer/media/media_stream_video_renderer_sink.cc
index 6ddf1eb1b8ba978cc25a1d896a22b718e84896e1..c7341aa8442590502307ce2bf7082c7c9a4f8a07 100644
--- a/content/renderer/media/media_stream_video_renderer_sink.cc
+++ b/content/renderer/media/media_stream_video_renderer_sink.cc
@@ -5,156 +5,310 @@
#include "content/renderer/media/media_stream_video_renderer_sink.h"
#include "base/feature_list.h"
+#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
+#include "content/child/child_process.h"
#include "content/public/common/content_features.h"
+#include "content/public/renderer/render_thread.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_util.h"
#include "media/renderers/gpu_video_accelerator_factories.h"
+#include "media/video/gpu_memory_buffer_video_frame_pool.h"
const int kMinFrameSize = 2;
namespace content {
+// FrameDeliverer is responsible for delivering frames received on
+// OnVideoFrame() to |repaint_cb_| on compositor thread.
+//
+// It is created on the main thread, but methods should be called and class
+// should be destructed on the compositor thread.
+class MediaStreamVideoRendererSink::FrameDeliverer {
+ public:
+ FrameDeliverer(const RepaintCB& repaint_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+ scoped_refptr<base::TaskRunner> worker_task_runner,
+ media::GpuVideoAcceleratorFactories* gpu_factories)
+ : repaint_cb_(repaint_cb),
+ state_(STOPPED),
+ frame_size_(kMinFrameSize, kMinFrameSize),
+ media_task_runner_(media_task_runner),
+ weak_factory_(this) {
+ compositor_thread_checker_.DetachFromThread();
+ weak_this_ = weak_factory_.GetWeakPtr();
+ if (gpu_factories &&
+ gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames() &&
+ base::FeatureList::IsEnabled(
+ features::kWebRtcUseGpuMemoryBufferVideoFrames)) {
+ gpu_memory_buffer_pool_.reset(new media::GpuMemoryBufferVideoFramePool(
+ media_task_runner, worker_task_runner, gpu_factories));
+ }
+ }
+
+ ~FrameDeliverer() {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ DCHECK(state_ == STARTED || state_ == PAUSED) << state_;
+
+ if (gpu_memory_buffer_pool_) {
+ media_task_runner_->DeleteSoon(FROM_HERE,
+ gpu_memory_buffer_pool_.release());
+ }
+ }
+
+ void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks /*current_time*/) {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ DCHECK(frame);
+ TRACE_EVENT_INSTANT1("webrtc",
+ "MediaStreamVideoRendererSink::"
+ "FrameDeliverer::OnVideoFrame",
+ TRACE_EVENT_SCOPE_THREAD, "timestamp",
+ frame->timestamp().InMilliseconds());
+
+ if (state_ != STARTED)
+ return;
+
+ if (!gpu_memory_buffer_pool_) {
+ FrameReady(frame);
+ return;
+ }
+
+ // |gpu_memory_buffer_pool_| deletion is going to be posted to
+ // |media_task_runner_|. base::Unretained() usage is fine since
+ // |gpu_memory_buffer_pool_| outlives the task.
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &media::GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
+ base::Unretained(gpu_memory_buffer_pool_.get()), frame,
+ media::BindToCurrentLoop(
+ base::Bind(&FrameDeliverer::FrameReady, weak_this_))));
+ }
+
+ void FrameReady(const scoped_refptr<media::VideoFrame>& frame) {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ DCHECK(frame);
+ TRACE_EVENT_INSTANT1(
+ "webrtc", "MediaStreamVideoRendererSink::FrameDeliverer::FrameReady",
+ TRACE_EVENT_SCOPE_THREAD, "timestamp",
+ frame->timestamp().InMilliseconds());
+
+ frame_size_ = frame->natural_size();
+ repaint_cb_.Run(frame);
+ }
+
+ void RenderEndOfStream() {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ // This is necessary to make sure audio can play if the video tag src is a
+ // MediaStream video track that has been rejected or ended. It also ensure
+ // that the renderer doesn't hold a reference to a real video frame if no
+ // more frames are provided. This is since there might be a finite number
+ // of available buffers. E.g, video that originates from a video camera.
+ scoped_refptr<media::VideoFrame> video_frame =
+ media::VideoFrame::CreateBlackFrame(
+ state_ == STOPPED ? gfx::Size(kMinFrameSize, kMinFrameSize)
+ : frame_size_);
+ video_frame->metadata()->SetBoolean(
+ media::VideoFrameMetadata::END_OF_STREAM, true);
+ video_frame->metadata()->SetTimeTicks(
+ media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks::Now());
+ OnVideoFrame(video_frame, base::TimeTicks());
+ }
+
+ void Start() {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, STOPPED);
+ state_ = STARTED;
+ }
+
+ void Resume() {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ if (state_ == PAUSED)
+ state_ = STARTED;
+ }
+
+ void Pause() {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ if (state_ == STARTED)
+ state_ = PAUSED;
+ }
+
+ VideoCaptureDeliverFrameCB GetDeliverFrameCallback() {
+ return base::Bind(&FrameDeliverer::OnVideoFrame, weak_this_);
+ }
+
+ private:
+ friend class MediaStreamVideoRendererSink;
+
+ void SetGpuMemoryBufferVideoForTesting(
+ media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) {
+ DCHECK(compositor_thread_checker_.CalledOnValidThread());
+ gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool);
+ }
+
+ const RepaintCB repaint_cb_;
+ State state_;
+ gfx::Size frame_size_;
+
+ // Pool of GpuMemoryBuffers and resources used to create hardware frames.
+ std::unique_ptr<media::GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool_;
+ const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+
+ // Used for DCHECKs to ensure method calls are executed on the correct thread.
+ base::ThreadChecker compositor_thread_checker_;
+
+ base::WeakPtr<FrameDeliverer> weak_this_;
+ base::WeakPtrFactory<FrameDeliverer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
+};
+
+// FrameReceiver is responsible for trampolining frames from IO thread to
+// the compositor thread by posting a task on |deliver_frame_cb_|.
+//
+// It is created on main thread, but methods should be called and class should
+// be destructed on the IO thread.
+class MediaStreamVideoRendererSink::FrameReceiver {
+ public:
+ FrameReceiver(
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const VideoCaptureDeliverFrameCB& deliver_frame_cb)
+ : compositor_task_runner_(compositor_task_runner),
+ deliver_frame_cb_(deliver_frame_cb) {
+ io_thread_checker_.DetachFromThread();
+ }
+
+ ~FrameReceiver() { DCHECK(io_thread_checker_.CalledOnValidThread()); }
+
+ void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks current_time) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(deliver_frame_cb_, frame, current_time));
+ }
+
+ private:
+ const scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+ const VideoCaptureDeliverFrameCB deliver_frame_cb_;
+
+ // Used for DCHECKs to ensure method calls executed in the correct thread.
+ base::ThreadChecker io_thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameReceiver);
+};
+
MediaStreamVideoRendererSink::MediaStreamVideoRendererSink(
const blink::WebMediaStreamTrack& video_track,
const base::Closure& error_cb,
const RepaintCB& repaint_cb,
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
const scoped_refptr<base::TaskRunner>& worker_task_runner,
media::GpuVideoAcceleratorFactories* gpu_factories)
: error_cb_(error_cb),
repaint_cb_(repaint_cb),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
- state_(STOPPED),
- frame_size_(kMinFrameSize, kMinFrameSize),
video_track_(video_track),
+ compositor_task_runner_(compositor_task_runner),
media_task_runner_(media_task_runner),
- weak_factory_(this) {
- if (gpu_factories &&
- gpu_factories->ShouldUseGpuMemoryBuffersForVideoFrames() &&
- base::FeatureList::IsEnabled(
- features::kWebRtcUseGpuMemoryBufferVideoFrames)) {
- gpu_memory_buffer_pool_.reset(new media::GpuMemoryBufferVideoFramePool(
- media_task_runner, worker_task_runner, gpu_factories));
- }
-}
+ worker_task_runner_(worker_task_runner),
+ gpu_factories_(gpu_factories) {}
-MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() {
- if (gpu_memory_buffer_pool_) {
- media_task_runner_->DeleteSoon(FROM_HERE,
- gpu_memory_buffer_pool_.release());
- }
-}
+MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() {}
void MediaStreamVideoRendererSink::Start() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(state_, STOPPED);
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ frame_deliverer_.reset(new MediaStreamVideoRendererSink::FrameDeliverer(
+ repaint_cb_, media_task_runner_, worker_task_runner_, gpu_factories_));
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::Start,
+ base::Unretained(frame_deliverer_.get())));
+
+ frame_receiver_.reset(new MediaStreamVideoRendererSink::FrameReceiver(
+ compositor_task_runner_, frame_deliverer_->GetDeliverFrameCallback()));
MediaStreamVideoSink::ConnectToTrack(
- video_track_, media::BindToCurrentLoop(
- base::Bind(&MediaStreamVideoRendererSink::OnVideoFrame,
- weak_factory_.GetWeakPtr())),
+ video_track_,
+ // This callback is run on IO thread. It is safe to use base::Unretained
+ // here because |frame_receiver_| will be destroyed after sink is
+ // disconnected from track.
+ base::Bind(&FrameReceiver::OnVideoFrame,
+ base::Unretained(frame_receiver_.get())),
// Local display video rendering is considered a secure link.
true);
- state_ = STARTED;
if (video_track_.source().getReadyState() ==
blink::WebMediaStreamSource::ReadyStateEnded ||
!video_track_.isEnabled()) {
- RenderSignalingFrame();
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::RenderEndOfStream,
+ base::Unretained(frame_deliverer_.get())));
}
}
void MediaStreamVideoRendererSink::Stop() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(state_ == STARTED || state_ == PAUSED);
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+
MediaStreamVideoSink::DisconnectFromTrack();
- weak_factory_.InvalidateWeakPtrs();
- state_ = STOPPED;
- frame_size_.set_width(kMinFrameSize);
- frame_size_.set_height(kMinFrameSize);
+ if (frame_receiver_) {
+ ChildProcess::current()->io_task_runner()->DeleteSoon(
+ FROM_HERE, frame_receiver_.release());
+ }
+ if (frame_deliverer_)
+ compositor_task_runner_->DeleteSoon(FROM_HERE, frame_deliverer_.release());
}
void MediaStreamVideoRendererSink::Resume() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- if (state_ == PAUSED)
- state_ = STARTED;
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (!frame_deliverer_)
+ return;
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::Resume,
+ base::Unretained(frame_deliverer_.get())));
}
void MediaStreamVideoRendererSink::Pause() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- if (state_ == STARTED)
- state_ = PAUSED;
-}
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (!frame_deliverer_)
+ return;
-void MediaStreamVideoRendererSink::SetGpuMemoryBufferVideoForTesting(
- media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) {
- gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool);
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::Pause,
+ base::Unretained(frame_deliverer_.get())));
}
void MediaStreamVideoRendererSink::OnReadyStateChanged(
blink::WebMediaStreamSource::ReadyState state) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- if (state == blink::WebMediaStreamSource::ReadyStateEnded)
- RenderSignalingFrame();
-}
-
-void MediaStreamVideoRendererSink::OnVideoFrame(
- const scoped_refptr<media::VideoFrame>& frame,
- base::TimeTicks estimated_capture_time) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(frame);
- if (state_ != STARTED)
- return;
-
- if (!gpu_memory_buffer_pool_) {
- FrameReady(frame);
- return;
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (state == blink::WebMediaStreamSource::ReadyStateEnded &&
+ frame_deliverer_) {
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::RenderEndOfStream,
+ base::Unretained(frame_deliverer_.get())));
}
-
- // |gpu_memory_buffer_pool_| deletion is going to be posted to
- // |media_task_runner_|. base::Unretained() usage is fine since
- // |gpu_memory_buffer_pool_| outlives the task.
- media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &media::GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
- base::Unretained(gpu_memory_buffer_pool_.get()), frame,
- media::BindToCurrentLoop(
- base::Bind(&MediaStreamVideoRendererSink::FrameReady,
- weak_factory_.GetWeakPtr()))));
}
-void MediaStreamVideoRendererSink::FrameReady(
- const scoped_refptr<media::VideoFrame>& frame) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(frame);
-
- frame_size_ = frame->natural_size();
- TRACE_EVENT_INSTANT1("media_stream_video_renderer_sink", "FrameReady",
- TRACE_EVENT_SCOPE_THREAD, "timestamp",
- frame->timestamp().InMilliseconds());
- repaint_cb_.Run(frame);
+MediaStreamVideoRendererSink::State
+MediaStreamVideoRendererSink::GetStateForTesting() {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (!frame_deliverer_)
+ return STOPPED;
+ return frame_deliverer_->state_;
}
-void MediaStreamVideoRendererSink::RenderSignalingFrame() {
- // This is necessary to make sure audio can play if the video tag src is
- // a MediaStream video track that has been rejected or ended.
- // It also ensure that the renderer don't hold a reference to a real video
- // frame if no more frames are provided. This is since there might be a
- // finite number of available buffers. E.g, video that
- // originates from a video camera.
- scoped_refptr<media::VideoFrame> video_frame =
- media::VideoFrame::CreateBlackFrame(frame_size_);
- video_frame->metadata()->SetBoolean(media::VideoFrameMetadata::END_OF_STREAM,
- true);
- video_frame->metadata()->SetTimeTicks(
- media::VideoFrameMetadata::REFERENCE_TIME, base::TimeTicks::Now());
- OnVideoFrame(video_frame, base::TimeTicks());
+void MediaStreamVideoRendererSink::SetGpuMemoryBufferVideoForTesting(
+ media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ CHECK(frame_deliverer_);
+ compositor_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FrameDeliverer::SetGpuMemoryBufferVideoForTesting,
+ base::Unretained(frame_deliverer_.get()),
+ gpu_memory_buffer_pool));
}
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698