SEA: Only spawn multi-layered encoders if active layers > 1.

With this CL, SimulcastEncoderAdapter no longer configures its encoder
as multi-layered if we only have a single active layer. Instead we
create a single single-layered encoder for that one and only active
layer. When using VP8 SW encoder this means that LibvpxVp8Encoder is
configured to only prepare a single video frame which avoids the cost of
scaling down to layers that we do not send. (A multi-layered
LibvpxVp8Encoder is required to scale even layers we don't encode.)

When profiling this CL I found very small but measurable gains for
representative downscale factors of 20.1 ms of 60 s profile. This is
just 0.0335% CPU so it's not much, but skipping a downscale might be
worth a lot more if we have to map/unmap buffers or do GPU round-trips
in the future (which I have not measured).

When downscaling to factors 4 and 2 due to libyuv having a
"fast-path" for these (i.e. no adaptation active), zero difference was
found for NV12. For I420 there was small regression of 16.1 ms
(0.026% CPU) for this one edge-case. It's possible to work around this,
but considering the tiny changes we're talking about, I really don't
think it's worth the additional complexity. I'll file a bug on libyuv
about scaling factors 2+2 vs 4 and leave it at that.

Bug: webrtc:12603
Change-Id: Id462140c6a829cf6b460baae868e94243f477db3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219683
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34092}
This commit is contained in:
Henrik Boström
2021-05-21 11:58:17 +02:00
committed by WebRTC LUCI CQ
parent bcadacdb0f
commit 70cd086644

View File

@ -344,20 +344,24 @@ int SimulcastEncoderAdapter::InitEncode(
// Two distinct scenarios:
// * Singlecast (total_streams_count == 1) or simulcast with simulcast-capable
// underlaying encoder implementation. SEA operates in bypass mode: original
// settings are passed to the underlaying encoder, frame encode complete
// callback is not intercepted.
// underlaying encoder implementation if active_streams_count > 1. SEA
// operates in bypass mode: original settings are passed to the underlaying
// encoder, frame encode complete callback is not intercepted.
// * Multi-encoder simulcast or singlecast if layers are deactivated
// (total_streams_count > 1 and active_streams_count >= 1). SEA creates
// N=active_streams_count encoders and configures each to produce a single
// stream.
// (active_streams_count >= 1). SEA creates N=active_streams_count encoders
// and configures each to produce a single stream.
int active_streams_count = CountActiveStreams(*inst);
// If we only have a single active layer it is better to create an encoder
// with only one configured layer than creating it with all-but-one disabled
// layers because that way we control scaling.
bool separate_encoders_needed =
!encoder_context->encoder().GetEncoderInfo().supports_simulcast ||
active_streams_count == 1;
// Singlecast or simulcast with simulcast-capable underlaying encoder.
if (total_streams_count_ == 1 ||
encoder_context->encoder().GetEncoderInfo().supports_simulcast) {
if (total_streams_count_ == 1 || !separate_encoders_needed) {
int ret = encoder_context->encoder().InitEncode(&codec_, settings);
if (ret >= 0) {
int active_streams_count = CountActiveStreams(*inst);
stream_contexts_.emplace_back(
/*parent=*/nullptr, std::move(encoder_context),
/*framerate_controller=*/nullptr, /*stream_idx=*/0, codec_.width,