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 |