Wire up webrtc::RateAllocation to RtcVideoEncoder.
This will be necessary in order to properly use temporal and spatial
scalability in hardware accelerated video codecs.
Bug: chromium:794608
Change-Id: I029e044a33c95eb4bcc9db04b028d04a3cb7f922
Reviewed-on: https://chromium-review.googlesource.com/1016900
Commit-Queue: Erik Språng <[email protected]>
Reviewed-by: Kentaro Hara <[email protected]>
Reviewed-by: Dale Curtis <[email protected]>
Reviewed-by: Emircan Uysaler <[email protected]>
Cr-Commit-Position: refs/heads/master@{#560254}
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index e1adb642..0fc9510b 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -743,8 +743,6 @@
"//third_party/webrtc/api:libjingle_peerconnection_api",
"//third_party/webrtc/api:optional",
"//third_party/webrtc/api:rtc_stats_api",
- "//third_party/webrtc/api/video:video_frame",
- "//third_party/webrtc/api/video:video_frame_i420",
"//third_party/webrtc/api/audio:aec3_factory",
"//third_party/webrtc/api/audio_codecs:audio_codecs_api",
"//third_party/webrtc/api/audio_codecs/L16:audio_decoder_L16",
@@ -757,6 +755,9 @@
"//third_party/webrtc/api/audio_codecs/isac:audio_encoder_isac",
"//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus",
"//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus",
+ "//third_party/webrtc/api/video:video_bitrate_allocation",
+ "//third_party/webrtc/api/video:video_frame",
+ "//third_party/webrtc/api/video:video_frame_i420",
"//third_party/webrtc/api/video_codecs:video_codecs_api",
"//third_party/webrtc/common_video:common_video",
"//third_party/webrtc/media:rtc_internal_video_codecs",
diff --git a/content/renderer/media/webrtc/rtc_video_encoder.cc b/content/renderer/media/webrtc/rtc_video_encoder.cc
index 02d58ce..bfa722c 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_encoder.cc
@@ -28,6 +28,7 @@
#include "content/renderer/media/webrtc/webrtc_video_frame_adapter.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/bitstream_buffer.h"
+#include "media/base/video_bitrate_allocation.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/video/gpu_video_accelerator_factories.h"
@@ -140,7 +141,8 @@
void UseOutputBitstreamBufferId(int32_t bitstream_buffer_id);
// Request encoding parameter change for the underlying encoder.
- void RequestEncodingParametersChange(uint32_t bitrate, uint32_t framerate);
+ void RequestEncodingParametersChange(webrtc::VideoBitrateAllocation bitrate,
+ uint32_t framerate);
void RegisterEncodeCompleteCallback(base::WaitableEvent* async_waiter,
int32_t* async_retval,
@@ -391,22 +393,41 @@
}
void RTCVideoEncoder::Impl::RequestEncodingParametersChange(
- uint32_t bitrate,
+ webrtc::VideoBitrateAllocation bitrate,
uint32_t framerate) {
- DVLOG(3) << __func__ << " bitrate=" << bitrate << ", framerate=" << framerate;
+ DVLOG(3) << __func__ << " bitrate=" << bitrate.ToString()
+ << ", framerate=" << framerate;
DCHECK(thread_checker_.CalledOnValidThread());
- // Check for overflow converting bitrate (kilobits/sec) to bits/sec.
- if (IsBitrateTooHigh(bitrate))
- return;
-
// This is a workaround to zero being temporarily provided, as part of the
// initial setup, by WebRTC.
- bitrate = std::max(1u, bitrate);
+ if (bitrate.get_sum_bps() == 0) {
+ bitrate.SetBitrate(0, 0, 1);
+ }
framerate = std::max(1u, framerate);
- if (video_encoder_)
- video_encoder_->RequestEncodingParametersChange(bitrate * 1000, framerate);
+ if (video_encoder_) {
+ media::VideoBitrateAllocation allocation;
+ for (size_t spatial_id = 0;
+ spatial_id < media::VideoBitrateAllocation::kMaxSpatialLayers;
+ ++spatial_id) {
+ for (size_t temporal_id = 0;
+ temporal_id < media::VideoBitrateAllocation::kMaxTemporalLayers;
+ ++temporal_id) {
+ // TODO(sprang): Clean this up if/when webrtc struct moves to int.
+ uint32_t layer_bitrate = bitrate.GetBitrate(spatial_id, temporal_id);
+ RTC_CHECK_LE(layer_bitrate,
+ static_cast<uint32_t>(std::numeric_limits<int>::max()));
+ if (!allocation.SetBitrate(spatial_id, temporal_id, layer_bitrate)) {
+ LOG(WARNING) << "Overflow in bitrate allocation: "
+ << bitrate.ToString();
+ break;
+ }
+ }
+ }
+ DCHECK_EQ(allocation.GetSumBps(), static_cast<int>(bitrate.get_sum_bps()));
+ video_encoder_->RequestEncodingParametersChange(allocation, framerate);
+ }
}
void RTCVideoEncoder::Impl::Destroy(base::WaitableEvent* async_waiter) {
@@ -922,8 +943,10 @@
return WEBRTC_VIDEO_CODEC_OK;
}
-int32_t RTCVideoEncoder::SetRates(uint32_t new_bit_rate, uint32_t frame_rate) {
- DVLOG(3) << __func__ << " new_bit_rate=" << new_bit_rate
+int32_t RTCVideoEncoder::SetRateAllocation(
+ const webrtc::VideoBitrateAllocation& allocation,
+ uint32_t frame_rate) {
+ DVLOG(3) << __func__ << " new_bit_rate=" << allocation.ToString()
<< ", frame_rate=" << frame_rate;
if (!impl_.get()) {
DVLOG(3) << "Encoder is not initialized";
@@ -939,7 +962,7 @@
gpu_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&RTCVideoEncoder::Impl::RequestEncodingParametersChange,
- impl_, new_bit_rate, frame_rate));
+ impl_, allocation, frame_rate));
return WEBRTC_VIDEO_CODEC_OK;
}
diff --git a/content/renderer/media/webrtc/rtc_video_encoder.h b/content/renderer/media/webrtc/rtc_video_encoder.h
index 3116e16..57ffd31 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder.h
+++ b/content/renderer/media/webrtc/rtc_video_encoder.h
@@ -18,6 +18,7 @@
#include "base/time/time.h"
#include "content/common/content_export.h"
#include "media/base/video_decoder_config.h"
+#include "third_party/webrtc/api/video/video_bitrate_allocation.h"
#include "third_party/webrtc/modules/video_coding/include/video_codec_interface.h"
#include "ui/gfx/geometry/size.h"
@@ -57,7 +58,8 @@
webrtc::EncodedImageCallback* callback) override;
int32_t Release() override;
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
- int32_t SetRates(uint32_t new_bit_rate, uint32_t frame_rate) override;
+ int32_t SetRateAllocation(const webrtc::VideoBitrateAllocation& allocation,
+ uint32_t framerate) override;
bool SupportsNativeHandle() const override;
const char* ImplementationName() const override;
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index ad4a75d..f0fa3368 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -251,6 +251,8 @@
"unaligned_shared_memory.h",
"user_input_monitor.cc",
"user_input_monitor.h",
+ "video_bitrate_allocation.cc",
+ "video_bitrate_allocation.h",
"video_codecs.cc",
"video_codecs.h",
"video_color_space.cc",
@@ -496,6 +498,7 @@
"unaligned_shared_memory_unittest.cc",
"user_input_monitor_unittest.cc",
"vector_math_unittest.cc",
+ "video_bitrate_allocation_unittest.cc",
"video_codecs_unittest.cc",
"video_color_space_unittest.cc",
"video_decoder_config_unittest.cc",
diff --git a/media/base/video_bitrate_allocation.cc b/media/base/video_bitrate_allocation.cc
new file mode 100644
index 0000000..629092e
--- /dev/null
+++ b/media/base/video_bitrate_allocation.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 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 "video_bitrate_allocation.h"
+
+#include <limits>
+#include <numeric>
+
+#include "base/logging.h"
+
+namespace media {
+
+constexpr size_t VideoBitrateAllocation::kMaxSpatialLayers;
+constexpr size_t VideoBitrateAllocation::kMaxTemporalLayers;
+
+VideoBitrateAllocation::VideoBitrateAllocation() : bitrates_{} {}
+
+bool VideoBitrateAllocation::SetBitrate(size_t spatial_index,
+ size_t temporal_index,
+ int bitrate_bps) {
+ CHECK_LT(spatial_index, kMaxSpatialLayers);
+ CHECK_LT(temporal_index, kMaxTemporalLayers);
+ CHECK_GE(bitrate_bps, 0);
+
+ if (GetSumBps() - bitrates_[spatial_index][temporal_index] >
+ std::numeric_limits<int>::max() - bitrate_bps) {
+ return false; // Would cause overflow of the sum.
+ }
+
+ bitrates_[spatial_index][temporal_index] = bitrate_bps;
+ return true;
+}
+
+int VideoBitrateAllocation::GetBitrateBps(size_t spatial_index,
+ size_t temporal_index) const {
+ CHECK_LT(spatial_index, kMaxSpatialLayers);
+ CHECK_LT(temporal_index, kMaxTemporalLayers);
+ return bitrates_[spatial_index][temporal_index];
+}
+
+int VideoBitrateAllocation::GetSumBps() const {
+ int sum = 0;
+ for (size_t spatial_index = 0; spatial_index < kMaxSpatialLayers;
+ ++spatial_index) {
+ for (size_t temporal_index = 0; temporal_index < kMaxTemporalLayers;
+ ++temporal_index) {
+ sum += bitrates_[spatial_index][temporal_index];
+ }
+ }
+ return sum;
+}
+
+} // namespace media
diff --git a/media/base/video_bitrate_allocation.h b/media/base/video_bitrate_allocation.h
new file mode 100644
index 0000000..6341ce3f
--- /dev/null
+++ b/media/base/video_bitrate_allocation.h
@@ -0,0 +1,43 @@
+// Copyright 2018 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 MEDIA_BASE_VIDEO_BITRATE_ALLOCATION_H_
+#define MEDIA_BASE_VIDEO_BITRATE_ALLOCATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Class that describes how video bitrate, in bps, is allocated across temporal
+// and spatial layers. Not that bitrates are NOT cumulative. Depending on if
+// layers are dependent or not, it is up to the user to aggregate.
+class MEDIA_EXPORT VideoBitrateAllocation {
+ public:
+ static constexpr size_t kMaxSpatialLayers = 5;
+ static constexpr size_t kMaxTemporalLayers = 4;
+
+ VideoBitrateAllocation();
+ ~VideoBitrateAllocation() = default;
+
+ // Returns if this bitrate can't be set (sum exceeds int max value).
+ bool SetBitrate(size_t spatial_index, size_t temporal_index, int bitrate_bps);
+
+ // Returns the bitrate for specified spatial/temporal index, or 0 if not set.
+ int GetBitrateBps(size_t spatial_index, size_t temporal_index) const;
+
+ // Sum of all bitrates.
+ int32_t GetSumBps() const;
+
+ private:
+ int bitrates_[kMaxSpatialLayers][kMaxTemporalLayers];
+ DISALLOW_COPY_AND_ASSIGN(VideoBitrateAllocation);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_VIDEO_BITRATE_ALLOCATION_H_
diff --git a/media/base/video_bitrate_allocation_unittest.cc b/media/base/video_bitrate_allocation_unittest.cc
new file mode 100644
index 0000000..ce9e8ac
--- /dev/null
+++ b/media/base/video_bitrate_allocation_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 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 <set>
+
+#include "base/logging.h"
+#include "media/base/video_bitrate_allocation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(VideoBitrateAllocationTest, SetAndGet) {
+ int sum = 0;
+ int layer_rate = 0;
+ VideoBitrateAllocation allocation;
+ for (size_t spatial_index = 0;
+ spatial_index < VideoBitrateAllocation::kMaxSpatialLayers;
+ ++spatial_index) {
+ for (size_t temporal_index = 0;
+ temporal_index < VideoBitrateAllocation::kMaxTemporalLayers;
+ ++temporal_index) {
+ sum += layer_rate;
+ EXPECT_TRUE(
+ allocation.SetBitrate(spatial_index, temporal_index, layer_rate++));
+ }
+ }
+ EXPECT_EQ(sum, allocation.GetSumBps());
+
+ layer_rate = 0;
+ for (size_t spatial_index = 0;
+ spatial_index < VideoBitrateAllocation::kMaxSpatialLayers;
+ ++spatial_index) {
+ for (size_t temporal_index = 0;
+ temporal_index < VideoBitrateAllocation::kMaxTemporalLayers;
+ ++temporal_index) {
+ EXPECT_EQ(allocation.GetBitrateBps(spatial_index, temporal_index),
+ layer_rate++);
+ }
+ }
+}
+
+TEST(VideoBitrateAllocationTest, CanSetMaxValue) {
+ VideoBitrateAllocation allocation;
+ // Single cell containing max value.
+ EXPECT_TRUE(allocation.SetBitrate(0, 0, std::numeric_limits<int>::max()));
+ // Setting to 0 is OK. Doesn't increase sum.
+ EXPECT_TRUE(allocation.SetBitrate(0, 1, 0));
+ // Adding 1 will overflow.
+ EXPECT_FALSE(allocation.SetBitrate(0, 2, 1));
+
+ EXPECT_EQ(std::numeric_limits<int>::max(), allocation.GetSumBps());
+}
+
+TEST(VideoBitrateAllocationTest, ValidatesSumWhenOverwriting) {
+ VideoBitrateAllocation allocation;
+ // Fill up to max sum.
+ EXPECT_TRUE(allocation.SetBitrate(0, 0, std::numeric_limits<int>::max() - 2));
+ EXPECT_TRUE(allocation.SetBitrate(0, 1, 2));
+ // This will overflow, old value kept.
+ EXPECT_FALSE(allocation.SetBitrate(0, 1, 3));
+ EXPECT_EQ(allocation.GetBitrateBps(0, 1), 2);
+ // OK only since we subtract the previous 2.
+ EXPECT_TRUE(allocation.SetBitrate(0, 1, 1));
+ EXPECT_EQ(allocation.GetBitrateBps(0, 1), 1);
+
+ EXPECT_EQ(std::numeric_limits<int>::max() - 1, allocation.GetSumBps());
+}
+
+} // namespace media
diff --git a/media/video/video_encode_accelerator.cc b/media/video/video_encode_accelerator.cc
index 4b344b7..1f6c0dcb 100644
--- a/media/video/video_encode_accelerator.cc
+++ b/media/video/video_encode_accelerator.cc
@@ -24,6 +24,12 @@
std::move(flush_callback).Run(false);
}
+void VideoEncodeAccelerator::RequestEncodingParametersChange(
+ const VideoBitrateAllocation& bitrate,
+ uint32_t framerate) {
+ RequestEncodingParametersChange(bitrate.GetSumBps(), framerate);
+}
+
} // namespace media
namespace std {
diff --git a/media/video/video_encode_accelerator.h b/media/video/video_encode_accelerator.h
index 0162dde..ddc1d77 100644
--- a/media/video/video_encode_accelerator.h
+++ b/media/video/video_encode_accelerator.h
@@ -17,6 +17,7 @@
#include "base/single_thread_task_runner.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/media_export.h"
+#include "media/base/video_bitrate_allocation.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
@@ -142,7 +143,7 @@
// |buffer| is the bitstream buffer to use for output.
virtual void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) = 0;
- // Request a change to the encoding parameters. This is only a request,
+ // Request a change to the encoding parameters. This is only a request,
// fulfilled on a best-effort basis.
// Parameters:
// |bitrate| is the requested new bitrate, in bits per second.
@@ -150,6 +151,16 @@
virtual void RequestEncodingParametersChange(uint32_t bitrate,
uint32_t framerate) = 0;
+ // Request a change to the encoding parameters. This is only a request,
+ // fulfilled on a best-effort basis. If not implemented, default behavior is
+ // to get the sum over layers and pass to version with bitrate as uint32_t.
+ // Parameters:
+ // |bitrate| is the requested new bitrate, per spatial and temporal layer.
+ // |framerate| is the requested new framerate, in frames per second.
+ virtual void RequestEncodingParametersChange(
+ const VideoBitrateAllocation& bitrate,
+ uint32_t framerate);
+
// Destroys the encoder: all pending inputs and outputs are dropped
// immediately and the component is freed. This call may asynchronously free
// system resources, but its client-visible effects are synchronous. After