Chromium Code Reviews| 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..1fa3ef90c475cc2d0a304e298f3fe95d36b8e910 100644 |
| --- a/content/renderer/media/media_stream_video_renderer_sink.cc |
| +++ b/content/renderer/media/media_stream_video_renderer_sink.cc |
| @@ -5,156 +5,306 @@ |
| #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 { |
| +// FrameDelivererOnCompositor is responsible for delivering frames received on |
| +// OnVideoFrame() to |repaint_cb_| on compositor thread. |
| +// |
| +// It is created on main thread, but methods should be called and class should |
| +// be destructed on compositor thread. |
| +class MediaStreamVideoRendererSink::FrameDelivererOnCompositor { |
| + public: |
| + FrameDelivererOnCompositor( |
| + const RepaintCB repaint_cb, |
| + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| + const 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_for_compositor_(this) { |
| + compositor_thread_checker_.DetachFromThread(); |
| + 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)); |
| + } |
| + } |
| + ~FrameDelivererOnCompositor() { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + |
| + 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); |
| + |
| + 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( |
| + &FrameDelivererOnCompositor::FrameReady, GetWeakPtr())))); |
| + } |
| + |
| + void FrameReady(const scoped_refptr<media::VideoFrame>& frame) { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + DCHECK(frame); |
|
perkj_chrome
2016/11/14 15:19:55
should you check the state here too? This frame ca
emircan
2016/11/14 22:43:32
Actually, we should check it. Thanks for catching
|
| + |
| + frame_size_ = frame->natural_size(); |
| + TRACE_EVENT_INSTANT1( |
| + "webrtc", |
| + "MediaStreamVideoRendererSink::FrameDelivererOnCompositor::FrameReady", |
| + TRACE_EVENT_SCOPE_THREAD, "timestamp", |
| + frame->timestamp().InMilliseconds()); |
| + repaint_cb_.Run(frame); |
| + } |
| + |
| + void RenderSignalingFrame() { |
| + 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 don't hold a reference to a real video |
|
perkj_chrome
2016/11/14 15:19:55
/s doesn't
emircan
2016/11/14 22:43:32
Done.
|
| + // 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()); |
| + FrameReady(video_frame); |
| + } |
| + |
| + void Start() { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + DCHECK_EQ(state_, STOPPED); |
| + state_ = STARTED; |
| + } |
| + |
| + void Stop() { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + DCHECK(state_ == STARTED || state_ == PAUSED); |
| + state_ = STOPPED; |
| + } |
| + |
| + void Resume() { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + if (state_ == PAUSED) |
| + state_ = STARTED; |
| + } |
| + |
| + void Pause() { |
| + DCHECK(compositor_thread_checker_.CalledOnValidThread()); |
| + if (state_ == STARTED) |
| + state_ = PAUSED; |
| + } |
| + |
| + base::WeakPtr<FrameDelivererOnCompositor> GetWeakPtr() { |
| + return weak_factory_for_compositor_.GetWeakPtr(); |
| + } |
| + |
| + 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 executed on the correct thread. |
|
perkj_chrome
2016/11/14 15:19:55
/s calls are
emircan
2016/11/14 22:43:32
Done.
|
| + base::ThreadChecker compositor_thread_checker_; |
| + |
| + base::WeakPtrFactory<FrameDelivererOnCompositor> weak_factory_for_compositor_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FrameDelivererOnCompositor); |
| +}; |
| + |
| +// FrameReceiverOnIO is responsible for trampolining frames from IO thread to |
| +// compositor thread by posting a task on |deliverer_|'s OnFrame() method. |
|
perkj_chrome
2016/11/14 15:19:55
/s to the compositor thread
emircan
2016/11/14 22:43:32
Done.
|
| +// |
| +// It is created on main thread, but methods should be called and class should |
| +// be destructed on IO thread. |
|
perkj_chrome
2016/11/14 15:19:55
/s the IO
emircan
2016/11/14 22:43:32
Done.
|
| +class MediaStreamVideoRendererSink::FrameReceiverOnIO { |
| + public: |
| + FrameReceiverOnIO( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner, |
| + const base::WeakPtr<FrameDelivererOnCompositor>& deliverer) |
| + : compositor_task_runner_(compositor_task_runner), |
| + deliverer_(deliverer), |
| + weak_factory_for_io_(this) { |
| + io_thread_checker_.DetachFromThread(); |
| + } |
| + ~FrameReceiverOnIO() { 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( |
|
perkj_chrome
2016/11/14 15:19:55
What is the most common case? with or without GPU
emircan
2016/11/14 22:43:32
Right now GpuMemoryBufferVideoFramePool is disable
|
| + FROM_HERE, base::Bind(&MediaStreamVideoRendererSink:: |
| + FrameDelivererOnCompositor::OnVideoFrame, |
| + deliverer_, frame, current_time)); |
| + } |
| + |
| + base::WeakPtr<FrameReceiverOnIO> GetWeakPtr() { |
| + return weak_factory_for_io_.GetWeakPtr(); |
| + } |
| + |
| + private: |
| + const scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; |
| + const base::WeakPtr<FrameDelivererOnCompositor> deliverer_; |
| + |
| + // Used for DCHECKs to ensure method calls executed in the correct thread. |
| + base::ThreadChecker io_thread_checker_; |
| + |
| + base::WeakPtrFactory<FrameReceiverOnIO> weak_factory_for_io_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FrameReceiverOnIO); |
| +}; |
| + |
| 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), |
| - 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)); |
| - } |
| -} |
| + frame_deliverer_( |
| + new MediaStreamVideoRendererSink::FrameDelivererOnCompositor( |
| + repaint_cb, |
| + media_task_runner, |
| + worker_task_runner, |
| + gpu_factories)), |
| + compositor_task_runner_(compositor_task_runner) {} |
| MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() { |
| - if (gpu_memory_buffer_pool_) { |
| - media_task_runner_->DeleteSoon(FROM_HERE, |
| - gpu_memory_buffer_pool_.release()); |
| - } |
| + compositor_task_runner_->DeleteSoon(FROM_HERE, frame_deliverer_.release()); |
| } |
| void MediaStreamVideoRendererSink::Start() { |
| - DCHECK(task_runner_->BelongsToCurrentThread()); |
| - DCHECK_EQ(state_, STOPPED); |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&FrameDelivererOnCompositor::Start, |
| + frame_deliverer_->GetWeakPtr())); |
| + |
| + frame_receiver_.reset(new MediaStreamVideoRendererSink::FrameReceiverOnIO( |
| + compositor_task_runner_, frame_deliverer_->GetWeakPtr())); |
| MediaStreamVideoSink::ConnectToTrack( |
| - video_track_, media::BindToCurrentLoop( |
| - base::Bind(&MediaStreamVideoRendererSink::OnVideoFrame, |
| - weak_factory_.GetWeakPtr())), |
| + video_track_, base::Bind(&FrameReceiverOnIO::OnVideoFrame, |
| + frame_receiver_->GetWeakPtr()), |
| // 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(&FrameDelivererOnCompositor::RenderSignalingFrame, |
| + frame_deliverer_->GetWeakPtr())); |
| } |
| } |
| 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()); |
| + } |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&FrameDelivererOnCompositor::Stop, |
| + frame_deliverer_->GetWeakPtr())); |
| } |
| void MediaStreamVideoRendererSink::Resume() { |
| - DCHECK(task_runner_->BelongsToCurrentThread()); |
| - if (state_ == PAUSED) |
| - state_ = STARTED; |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&FrameDelivererOnCompositor::Resume, |
| + frame_deliverer_->GetWeakPtr())); |
| } |
| void MediaStreamVideoRendererSink::Pause() { |
| - DCHECK(task_runner_->BelongsToCurrentThread()); |
| - if (state_ == STARTED) |
| - state_ = PAUSED; |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&FrameDelivererOnCompositor::Pause, |
| + frame_deliverer_->GetWeakPtr())); |
| +} |
| + |
| +MediaStreamVideoRendererSink::State |
| +MediaStreamVideoRendererSink::GetStateForTesting() { |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + return frame_deliverer_->state_; |
| } |
| void MediaStreamVideoRendererSink::SetGpuMemoryBufferVideoForTesting( |
| media::GpuMemoryBufferVideoFramePool* gpu_memory_buffer_pool) { |
| - gpu_memory_buffer_pool_.reset(gpu_memory_buffer_pool); |
| + DCHECK(main_thread_checker_.CalledOnValidThread()); |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&FrameDelivererOnCompositor::SetGpuMemoryBufferVideoForTesting, |
| + frame_deliverer_->GetWeakPtr(), gpu_memory_buffer_pool)); |
| } |
| 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) { |
| + compositor_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&FrameDelivererOnCompositor::RenderSignalingFrame, |
| + frame_deliverer_->GetWeakPtr())); |
| } |
| - |
| - // |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); |
| -} |
| - |
| -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()); |
| } |
| } // namespace content |