blob: 94687b8030479af0fe40447bae3d32af9f60a48f [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/mediastream/media_stream_video_renderer_sink.h"
#include <utility>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_util.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
const int kMinFrameSize = 2;
namespace blink {
// FrameDeliverer is responsible for delivering frames received on
// OnVideoFrame() to |repaint_cb_| on the IO thread.
//
// It is created on the main thread, but methods should be called and class
// should be destructed on the IO thread.
class MediaStreamVideoRendererSink::FrameDeliverer {
public:
FrameDeliverer(
const RepaintCB& repaint_cb,
base::WeakPtr<MediaStreamVideoRendererSink>
media_stream_video_renderer_sink,
scoped_refptr<base::SingleThreadTaskRunner> main_render_task_runner)
: main_render_task_runner_(std::move(main_render_task_runner)),
repaint_cb_(repaint_cb),
media_stream_video_renderer_sink_(media_stream_video_renderer_sink),
state_(STOPPED),
frame_size_(kMinFrameSize, kMinFrameSize),
emit_frame_drop_events_(true) {
DETACH_FROM_THREAD(io_thread_checker_);
}
~FrameDeliverer() {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
DCHECK(state_ == STARTED || state_ == PAUSED) << state_;
}
void OnVideoFrame(scoped_refptr<media::VideoFrame> frame,
base::TimeTicks /*current_time*/) {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
DCHECK(frame);
TRACE_EVENT_INSTANT1("webrtc",
"MediaStreamVideoRendererSink::"
"FrameDeliverer::OnVideoFrame",
TRACE_EVENT_SCOPE_THREAD, "timestamp",
frame->timestamp().InMilliseconds());
if (state_ != STARTED) {
if (emit_frame_drop_events_) {
emit_frame_drop_events_ = false;
PostCrossThreadTask(
*main_render_task_runner_, FROM_HERE,
CrossThreadBindOnce(&MediaStreamVideoRendererSink::OnFrameDropped,
media_stream_video_renderer_sink_,
media::VideoCaptureFrameDropReason::
kRendererSinkFrameDelivererIsNotStarted));
}
return;
}
frame_size_ = frame->natural_size();
repaint_cb_.Run(std::move(frame));
}
void RenderEndOfStream() {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
// 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_);
if (!video_frame)
return;
video_frame->metadata().end_of_stream = true;
video_frame->metadata().reference_time = base::TimeTicks::Now();
OnVideoFrame(video_frame, base::TimeTicks());
}
void Start() {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
DCHECK_EQ(state_, STOPPED);
SetState(STARTED);
}
void Resume() {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
if (state_ == PAUSED)
SetState(STARTED);
}
void Pause() {
DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
if (state_ == STARTED)
SetState(PAUSED);
}
private:
void SetState(State target_state) {
state_ = target_state;
emit_frame_drop_events_ = true;
}
friend class MediaStreamVideoRendererSink;
const scoped_refptr<base::SingleThreadTaskRunner> main_render_task_runner_;
const RepaintCB repaint_cb_;
base::WeakPtr<MediaStreamVideoRendererSink> media_stream_video_renderer_sink_;
State state_;
gfx::Size frame_size_;
bool emit_frame_drop_events_;
// Used for DCHECKs to ensure method calls are executed on the correct thread.
THREAD_CHECKER(io_thread_checker_);
DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
};
MediaStreamVideoRendererSink::MediaStreamVideoRendererSink(
MediaStreamComponent* video_component,
const RepaintCB& repaint_cb,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> main_render_task_runner)
: repaint_cb_(repaint_cb),
video_component_(video_component),
io_task_runner_(std::move(io_task_runner)),
main_render_task_runner_(std::move(main_render_task_runner)) {}
MediaStreamVideoRendererSink::~MediaStreamVideoRendererSink() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
}
void MediaStreamVideoRendererSink::Start() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
frame_deliverer_.reset(new MediaStreamVideoRendererSink::FrameDeliverer(
repaint_cb_, weak_factory_.GetWeakPtr(), main_render_task_runner_));
PostCrossThreadTask(
*io_task_runner_, FROM_HERE,
CrossThreadBindOnce(&FrameDeliverer::Start,
WTF::CrossThreadUnretained(frame_deliverer_.get())));
MediaStreamVideoSink::ConnectToTrack(
WebMediaStreamTrack(video_component_.Get()),
// This callback is run on IO thread. It is safe to use base::Unretained
// here because |frame_receiver_| will be destroyed on IO thread after
// sink is disconnected from track.
ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
&FrameDeliverer::OnVideoFrame,
WTF::CrossThreadUnretained(frame_deliverer_.get()))),
// Local display video rendering is considered a secure link.
true);
if (video_component_->Source()->GetReadyState() ==
MediaStreamSource::kReadyStateEnded ||
!video_component_->Enabled()) {
PostCrossThreadTask(
*io_task_runner_, FROM_HERE,
CrossThreadBindOnce(&FrameDeliverer::RenderEndOfStream,
CrossThreadUnretained(frame_deliverer_.get())));
}
}
void MediaStreamVideoRendererSink::Stop() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
MediaStreamVideoSink::DisconnectFromTrack();
if (frame_deliverer_)
io_task_runner_->DeleteSoon(FROM_HERE, frame_deliverer_.release());
}
void MediaStreamVideoRendererSink::Resume() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
if (!frame_deliverer_)
return;
PostCrossThreadTask(*io_task_runner_, FROM_HERE,
WTF::CrossThreadBindOnce(
&FrameDeliverer::Resume,
WTF::CrossThreadUnretained(frame_deliverer_.get())));
}
void MediaStreamVideoRendererSink::Pause() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
if (!frame_deliverer_)
return;
PostCrossThreadTask(*io_task_runner_, FROM_HERE,
WTF::CrossThreadBindOnce(
&FrameDeliverer::Pause,
WTF::CrossThreadUnretained(frame_deliverer_.get())));
}
void MediaStreamVideoRendererSink::OnReadyStateChanged(
WebMediaStreamSource::ReadyState state) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
if (state == WebMediaStreamSource::kReadyStateEnded && frame_deliverer_) {
PostCrossThreadTask(
*io_task_runner_, FROM_HERE,
WTF::CrossThreadBindOnce(
&FrameDeliverer::RenderEndOfStream,
WTF::CrossThreadUnretained(frame_deliverer_.get())));
}
}
MediaStreamVideoRendererSink::State
MediaStreamVideoRendererSink::GetStateForTesting() {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
if (!frame_deliverer_)
return STOPPED;
return frame_deliverer_->state_;
}
} // namespace blink