Make WebrtcVideoEncoder asynchronous.
Previously WebrtcVideoEncoder interface was synchronous, so it was
incompatible with HW encoders. Refactored it to make it asynchronous.
Change-Id: I1b0f3ce6dda657ecddb4ac84cf8132a22b473308
Reviewed-on: https://chromium-review.googlesource.com/541122
Reviewed-by: Scott Nichols <[email protected]>
Commit-Queue: Sergey Ulanov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#481297}
diff --git a/remoting/codec/BUILD.gn b/remoting/codec/BUILD.gn
index 85896de..1eee958 100644
--- a/remoting/codec/BUILD.gn
+++ b/remoting/codec/BUILD.gn
@@ -15,6 +15,8 @@
"video_encoder_verbatim.h",
"video_encoder_vpx.cc",
"video_encoder_vpx.h",
+ "webrtc_video_encoder_proxy.cc",
+ "webrtc_video_encoder_proxy.h",
"webrtc_video_encoder_vpx.cc",
"webrtc_video_encoder_vpx.h",
]
diff --git a/remoting/codec/webrtc_video_encoder.h b/remoting/codec/webrtc_video_encoder.h
index bf097c2..09d53e2 100644
--- a/remoting/codec/webrtc_video_encoder.h
+++ b/remoting/codec/webrtc_video_encoder.h
@@ -51,16 +51,21 @@
int quantizer;
};
+ typedef base::OnceCallback<void(std::unique_ptr<EncodedFrame>)>
+ EncodeCallback;
+
virtual ~WebrtcVideoEncoder() {}
// Encode an image stored in |frame|. If frame.updated_region() is empty
// then the encoder may return a frame (e.g. to top-off previously-encoded
// portions of the frame to higher quality) or return nullptr to indicate that
- // there is no work to do. |frame| may be nullptr. This case must be handled
- // the same as if frame.updated_region() is empty.
- virtual std::unique_ptr<EncodedFrame> Encode(
- const webrtc::DesktopFrame* frame,
- const FrameParams& param) = 0;
+ // there is no work to do. |frame| may be nullptr, which is equivalent to a
+ // frame with an empty updated_region(). |done| callback may be called
+ // synchronously. It must not be called if the encoder is destroyed while
+ // request is pending.
+ virtual void Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& param,
+ EncodeCallback done) = 0;
};
} // namespace remoting
diff --git a/remoting/codec/webrtc_video_encoder_proxy.cc b/remoting/codec/webrtc_video_encoder_proxy.cc
new file mode 100644
index 0000000..4ef47cc
--- /dev/null
+++ b/remoting/codec/webrtc_video_encoder_proxy.cc
@@ -0,0 +1,69 @@
+// Copyright 2017 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 "remoting/codec/webrtc_video_encoder_proxy.h"
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+
+namespace remoting {
+
+class WebrtcVideoEncoderProxy::Core {
+ public:
+ Core(std::unique_ptr<WebrtcVideoEncoder> encoder)
+ : encoder_(std::move(encoder)),
+ main_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+ ~Core() {}
+
+ void Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& params,
+ EncodeCallback done) {
+ encoder_->Encode(std::move(frame), params,
+ base::BindOnce(&Core::OnEncoded, base::Unretained(this),
+ std::move(done)));
+ }
+
+ private:
+ void OnEncoded(EncodeCallback done,
+ std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame) {
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(done), std::move(frame)));
+ }
+
+ std::unique_ptr<WebrtcVideoEncoder> encoder_;
+ scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
+};
+
+WebrtcVideoEncoderProxy::WebrtcVideoEncoderProxy(
+ std::unique_ptr<WebrtcVideoEncoder> encoder,
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner)
+ : core_(new Core(std::move(encoder))),
+ encode_task_runner_(encode_task_runner),
+ weak_factory_(this) {}
+
+WebrtcVideoEncoderProxy::~WebrtcVideoEncoderProxy() {
+ encode_task_runner_->DeleteSoon(FROM_HERE, core_.release());
+}
+
+void WebrtcVideoEncoderProxy::Encode(
+ std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& params,
+ EncodeCallback done) {
+ encode_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &Core::Encode, base::Unretained(core_.get()), std::move(frame),
+ params,
+ base::BindOnce(&WebrtcVideoEncoderProxy::OnEncoded,
+ weak_factory_.GetWeakPtr(), std::move(done))));
+}
+
+void WebrtcVideoEncoderProxy::OnEncoded(
+ EncodeCallback done,
+ std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame) {
+ std::move(done).Run(std::move(frame));
+}
+
+} // namespace remoting
diff --git a/remoting/codec/webrtc_video_encoder_proxy.h b/remoting/codec/webrtc_video_encoder_proxy.h
new file mode 100644
index 0000000..bcd261f
--- /dev/null
+++ b/remoting/codec/webrtc_video_encoder_proxy.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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.
+
+#ifndef REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_PROXY_H_
+#define REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_PROXY_H_
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "remoting/codec/webrtc_video_encoder.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
+namespace remoting {
+
+// WebrtcVideoEncoder implementation that runs encoder on a background thread.
+class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder {
+ public:
+ WebrtcVideoEncoderProxy(
+ std::unique_ptr<WebrtcVideoEncoder> encoder,
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner);
+ ~WebrtcVideoEncoderProxy() override;
+
+ // WebrtcVideoEncoder interface.
+ void Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& params,
+ EncodeCallback done) override;
+
+ private:
+ class Core;
+
+ void OnEncoded(EncodeCallback done,
+ std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame);
+
+ std::unique_ptr<Core> core_;
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner_;
+ base::WeakPtrFactory<WebrtcVideoEncoderProxy> weak_factory_;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_CODEC_WEBRTC_VIDEO_ENCODER_PROXY_H_
diff --git a/remoting/codec/webrtc_video_encoder_vpx.cc b/remoting/codec/webrtc_video_encoder_vpx.cc
index da198feb..28b2bf1 100644
--- a/remoting/codec/webrtc_video_encoder_vpx.cc
+++ b/remoting/codec/webrtc_video_encoder_vpx.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/sys_info.h"
@@ -273,9 +274,9 @@
}
}
-std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> WebrtcVideoEncoderVpx::Encode(
- const webrtc::DesktopFrame* frame,
- const FrameParams& params) {
+void WebrtcVideoEncoderVpx::Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& params,
+ EncodeCallback done) {
webrtc::DesktopSize previous_frame_size =
image_ ? webrtc::DesktopSize(image_->w, image_->h)
: webrtc::DesktopSize();
@@ -284,7 +285,8 @@
// Don't need to send anything until we get the first non-null frame.
if (frame_size.is_empty()) {
- return nullptr;
+ std::move(done).Run(nullptr);
+ return;
}
DCHECK_GE(frame_size.width(), 32);
@@ -304,7 +306,7 @@
webrtc::DesktopRegion updated_region;
// Convert the updated capture data ready for encode.
- PrepareImage(frame, &updated_region);
+ PrepareImage(frame.get(), &updated_region);
// Update active map based on updated region.
if (params.clear_active_map)
@@ -373,7 +375,7 @@
}
}
- return encoded_frame;
+ std::move(done).Run(std::move(encoded_frame));
}
WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9)
diff --git a/remoting/codec/webrtc_video_encoder_vpx.h b/remoting/codec/webrtc_video_encoder_vpx.h
index 5c5cfd4..a657497 100644
--- a/remoting/codec/webrtc_video_encoder_vpx.h
+++ b/remoting/codec/webrtc_video_encoder_vpx.h
@@ -41,8 +41,9 @@
void SetLosslessColor(bool want_lossless);
// WebrtcVideoEncoder interface.
- std::unique_ptr<EncodedFrame> Encode(const webrtc::DesktopFrame* frame,
- const FrameParams& params) override;
+ void Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
+ const FrameParams& params,
+ EncodeCallback done) override;
private:
explicit WebrtcVideoEncoderVpx(bool use_vp9);
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index 0167d3a..ac8ffdf 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -7,10 +7,9 @@
#include <utility>
#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/memory/ptr_util.h"
#include "remoting/base/constants.h"
+#include "remoting/codec/webrtc_video_encoder_proxy.h"
#include "remoting/codec/webrtc_video_encoder_vpx.h"
#include "remoting/protocol/frame_stats.h"
#include "remoting/protocol/host_video_stats_dispatcher.h"
@@ -42,11 +41,6 @@
uint32_t capturer_id = 0;
};
-struct WebrtcVideoStream::EncodedFrameWithStats {
- std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame;
- std::unique_ptr<FrameStats> stats;
-};
-
WebrtcVideoStream::WebrtcVideoStream()
: video_stats_dispatcher_(kStreamLabel), weak_factory_(this) {}
@@ -58,13 +52,12 @@
}
peer_connection_->RemoveStream(stream_.get());
}
- encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release());
}
void WebrtcVideoStream::Start(
std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer,
WebrtcTransport* webrtc_transport,
- scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner) {
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(webrtc_transport);
DCHECK(desktop_capturer);
@@ -147,8 +140,8 @@
std::unique_ptr<webrtc::DesktopFrame> frame) {
DCHECK(thread_checker_.CalledOnValidThread());
- captured_frame_stats_->capture_ended_time = base::TimeTicks::Now();
- captured_frame_stats_->capture_delay =
+ current_frame_stats_->capture_ended_time = base::TimeTicks::Now();
+ current_frame_stats_->capture_delay =
base::TimeDelta::FromMilliseconds(frame ? frame->capture_time_ms() : 0);
WebrtcVideoEncoder::FrameParams frame_params;
@@ -168,17 +161,15 @@
if (observer_)
observer_->OnVideoSizeChanged(this, frame_size_, frame_dpi_);
}
+
+ current_frame_stats_->capturer_id = frame->capturer_id();
}
DCHECK(encoder_);
-
- base::PostTaskAndReplyWithResult(
- encode_task_runner_.get(), FROM_HERE,
- base::Bind(&WebrtcVideoStream::EncodeFrame, encoder_.get(),
- base::Passed(std::move(frame)), frame_params,
- base::Passed(std::move(captured_frame_stats_))),
- base::Bind(&WebrtcVideoStream::OnFrameEncoded,
- weak_factory_.GetWeakPtr()));
+ current_frame_stats_->encode_started_time = base::TimeTicks::Now();
+ encoder_->Encode(
+ std::move(frame), frame_params,
+ base::Bind(&WebrtcVideoStream::OnFrameEncoded, base::Unretained(this)));
}
void WebrtcVideoStream::OnChannelInitialized(
@@ -194,44 +185,30 @@
void WebrtcVideoStream::CaptureNextFrame() {
DCHECK(thread_checker_.CalledOnValidThread());
- captured_frame_stats_.reset(new FrameStats());
- captured_frame_stats_->capture_started_time = base::TimeTicks::Now();
- captured_frame_stats_->input_event_timestamps =
+ current_frame_stats_.reset(new FrameStats());
+ current_frame_stats_->capture_started_time = base::TimeTicks::Now();
+ current_frame_stats_->input_event_timestamps =
event_timestamps_source_->TakeLastEventTimestamps();
capturer_->CaptureFrame();
}
-// static
-WebrtcVideoStream::EncodedFrameWithStats WebrtcVideoStream::EncodeFrame(
- WebrtcVideoEncoder* encoder,
- std::unique_ptr<webrtc::DesktopFrame> frame,
- WebrtcVideoEncoder::FrameParams params,
- std::unique_ptr<WebrtcVideoStream::FrameStats> stats) {
- EncodedFrameWithStats result;
- result.stats = std::move(stats);
- result.stats->encode_started_time = base::TimeTicks::Now();
- result.frame = encoder->Encode(frame.get(), params);
- result.stats->encode_ended_time = base::TimeTicks::Now();
- if (frame) {
- result.stats->capturer_id = frame->capturer_id();
- }
- return result;
-}
-
-void WebrtcVideoStream::OnFrameEncoded(EncodedFrameWithStats frame) {
+void WebrtcVideoStream::OnFrameEncoded(
+ std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame) {
DCHECK(thread_checker_.CalledOnValidThread());
- HostFrameStats stats;
- scheduler_->OnFrameEncoded(frame.frame.get(), &stats);
+ current_frame_stats_->encode_ended_time = base::TimeTicks::Now();
- if (!frame.frame) {
+ HostFrameStats stats;
+ scheduler_->OnFrameEncoded(frame.get(), &stats);
+
+ if (!frame) {
return;
}
webrtc::EncodedImageCallback::Result result =
webrtc_transport_->video_encoder_factory()->SendEncodedFrame(
- *frame.frame, frame.stats->capture_started_time);
+ *frame, current_frame_stats_->capture_started_time);
if (result.error != webrtc::EncodedImageCallback::Result::OK) {
// TODO(sergeyu): Stop the stream.
LOG(ERROR) << "Failed to send video frame.";
@@ -240,30 +217,31 @@
// Send FrameStats message.
if (video_stats_dispatcher_.is_connected()) {
- stats.frame_size = frame.frame ? frame.frame->data.size() : 0;
+ stats.frame_size = frame ? frame->data.size() : 0;
- if (!frame.stats->input_event_timestamps.is_null()) {
+ if (!current_frame_stats_->input_event_timestamps.is_null()) {
stats.capture_pending_delay =
- frame.stats->capture_started_time -
- frame.stats->input_event_timestamps.host_timestamp;
+ current_frame_stats_->capture_started_time -
+ current_frame_stats_->input_event_timestamps.host_timestamp;
stats.latest_event_timestamp =
- frame.stats->input_event_timestamps.client_timestamp;
+ current_frame_stats_->input_event_timestamps.client_timestamp;
}
- stats.capture_delay = frame.stats->capture_delay;
+ stats.capture_delay = current_frame_stats_->capture_delay;
// Total overhead time for IPC and threading when capturing frames.
stats.capture_overhead_delay =
- (frame.stats->capture_ended_time - frame.stats->capture_started_time) -
+ (current_frame_stats_->capture_ended_time -
+ current_frame_stats_->capture_started_time) -
stats.capture_delay;
- stats.encode_pending_delay =
- frame.stats->encode_started_time - frame.stats->capture_ended_time;
+ stats.encode_pending_delay = current_frame_stats_->encode_started_time -
+ current_frame_stats_->capture_ended_time;
- stats.encode_delay =
- frame.stats->encode_ended_time - frame.stats->encode_started_time;
+ stats.encode_delay = current_frame_stats_->encode_ended_time -
+ current_frame_stats_->encode_started_time;
- stats.capturer_id = frame.stats->capturer_id;
+ stats.capturer_id = current_frame_stats_->capturer_id;
video_stats_dispatcher_.OnVideoFrameStats(result.frame_id, stats);
}
@@ -272,9 +250,11 @@
void WebrtcVideoStream::OnEncoderCreated(webrtc::VideoCodecType codec_type) {
DCHECK(thread_checker_.CalledOnValidThread());
if (codec_type == webrtc::kVideoCodecVP8) {
- encoder_ = WebrtcVideoEncoderVpx::CreateForVP8();
+ encoder_ = base::MakeUnique<WebrtcVideoEncoderProxy>(
+ WebrtcVideoEncoderVpx::CreateForVP8(), encode_task_runner_);
} else if (codec_type == webrtc::kVideoCodecVP9) {
- encoder_ = WebrtcVideoEncoderVpx::CreateForVP9();
+ encoder_ = base::MakeUnique<WebrtcVideoEncoderProxy>(
+ WebrtcVideoEncoderVpx::CreateForVP9(), encode_task_runner_);
} else {
LOG(FATAL) << "Unknown codec type: " << codec_type;
}
diff --git a/remoting/protocol/webrtc_video_stream.h b/remoting/protocol/webrtc_video_stream.h
index 8b7998ca..64578b48 100644
--- a/remoting/protocol/webrtc_video_stream.h
+++ b/remoting/protocol/webrtc_video_stream.h
@@ -42,7 +42,7 @@
void Start(std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer,
WebrtcTransport* webrtc_transport,
- scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner);
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner);
// VideoStream interface.
void SetEventTimestampsSource(scoped_refptr<InputEventTimestampsSource>
@@ -54,7 +54,6 @@
private:
struct FrameStats;
- struct EncodedFrameWithStats;
// webrtc::DesktopCapturer::Callback interface.
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
@@ -67,13 +66,7 @@
// Called by the |scheduler_|.
void CaptureNextFrame();
- // Task running on the encoder thread to encode the |frame|.
- static EncodedFrameWithStats EncodeFrame(
- WebrtcVideoEncoder* encoder,
- std::unique_ptr<webrtc::DesktopFrame> frame,
- WebrtcVideoEncoder::FrameParams params,
- std::unique_ptr<WebrtcVideoStream::FrameStats> stats);
- void OnFrameEncoded(EncodedFrameWithStats frame);
+ void OnFrameEncoded(std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> frame);
void OnEncoderCreated(webrtc::VideoCodecType codec_type);
@@ -82,7 +75,7 @@
// Used to send across encoded frames.
WebrtcTransport* webrtc_transport_ = nullptr;
// Task runner used to run |encoder_|.
- scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> encode_task_runner_;
// Used to encode captured frames. Always accessed on the encode thread.
std::unique_ptr<WebrtcVideoEncoder> encoder_;
@@ -94,7 +87,7 @@
HostVideoStatsDispatcher video_stats_dispatcher_;
// Stats of the frame that's being captured.
- std::unique_ptr<FrameStats> captured_frame_stats_;
+ std::unique_ptr<FrameStats> current_frame_stats_;
std::unique_ptr<WebrtcFrameScheduler> scheduler_;