Extract bitrate allocation of spatial/temporal layers out of codec impl.
This CL makes a number of intervowen changes: * Add BitrateAllocation struct, that contains a codec independent view of how the target bitrate is distributed over spatial and temporal layers. * Adds the BitrateAllocator interface, which takes a bitrate and frame rate and produces a BitrateAllocation. * A default (non layered) implementation is added, and SimulcastRateAllocator is extended to fully handle VP8 allocation. This includes capturing TemporalLayer instances created by the encoder. * ViEEncoder now owns both the bitrate allocator and the temporal layer factories for VP8. This allows allocation to happen fully outside of the encoder implementation. This refactoring will make it possible for ViEEncoder to signal the full picture of target bitrates to the RTCP module. BUG=webrtc:6301 Review-Url: https://codereview.webrtc.org/2434073003 Cr-Commit-Position: refs/heads/master@{#14998}
This commit is contained in:
parent
592baaf89a
commit
8f46c679d2
@ -112,7 +112,8 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
|
|||||||
int32_t Release() override;
|
int32_t Release() override;
|
||||||
int32_t SetChannelParameters(uint32_t /* packet_loss */,
|
int32_t SetChannelParameters(uint32_t /* packet_loss */,
|
||||||
int64_t /* rtt */) override;
|
int64_t /* rtt */) override;
|
||||||
int32_t SetRates(uint32_t new_bit_rate, uint32_t frame_rate) override;
|
int32_t SetRateAllocation(const webrtc::BitrateAllocation& rate_allocation,
|
||||||
|
uint32_t frame_rate) override;
|
||||||
|
|
||||||
// rtc::MessageHandler implementation.
|
// rtc::MessageHandler implementation.
|
||||||
void OnMessage(rtc::Message* msg) override;
|
void OnMessage(rtc::Message* msg) override;
|
||||||
@ -465,11 +466,12 @@ int32_t MediaCodecVideoEncoder::SetChannelParameters(uint32_t /* packet_loss */,
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t MediaCodecVideoEncoder::SetRates(uint32_t new_bit_rate,
|
int32_t MediaCodecVideoEncoder::SetRateAllocation(
|
||||||
uint32_t frame_rate) {
|
const webrtc::BitrateAllocation& rate_allocation,
|
||||||
|
uint32_t frame_rate) {
|
||||||
return codec_thread_->Invoke<int32_t>(
|
return codec_thread_->Invoke<int32_t>(
|
||||||
RTC_FROM_HERE, Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread, this,
|
RTC_FROM_HERE, Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread, this,
|
||||||
new_bit_rate, frame_rate));
|
rate_allocation.get_sum_kbps(), frame_rate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
|
void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -59,6 +60,7 @@ VideoCodec::VideoCodec()
|
|||||||
simulcastStream(),
|
simulcastStream(),
|
||||||
spatialLayers(),
|
spatialLayers(),
|
||||||
mode(kRealtimeVideo),
|
mode(kRealtimeVideo),
|
||||||
|
expect_encode_from_texture(false),
|
||||||
codecSpecific() {}
|
codecSpecific() {}
|
||||||
|
|
||||||
VideoCodecVP8* VideoCodec::VP8() {
|
VideoCodecVP8* VideoCodec::VP8() {
|
||||||
@ -91,4 +93,89 @@ const VideoCodecH264& VideoCodec::H264() const {
|
|||||||
return codecSpecific.H264;
|
return codecSpecific.H264;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* kPayloadNameVp8 = "VP8";
|
||||||
|
static const char* kPayloadNameVp9 = "VP9";
|
||||||
|
static const char* kPayloadNameH264 = "H264";
|
||||||
|
static const char* kPayloadNameI420 = "I420";
|
||||||
|
static const char* kPayloadNameRED = "RED";
|
||||||
|
static const char* kPayloadNameULPFEC = "ULPFEC";
|
||||||
|
static const char* kPayloadNameGeneric = "Generic";
|
||||||
|
|
||||||
|
rtc::Optional<std::string> CodecTypeToPayloadName(VideoCodecType type) {
|
||||||
|
switch (type) {
|
||||||
|
case kVideoCodecVP8:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameVp8);
|
||||||
|
case kVideoCodecVP9:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameVp9);
|
||||||
|
case kVideoCodecH264:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameH264);
|
||||||
|
case kVideoCodecI420:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameI420);
|
||||||
|
case kVideoCodecRED:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameRED);
|
||||||
|
case kVideoCodecULPFEC:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameULPFEC);
|
||||||
|
case kVideoCodecGeneric:
|
||||||
|
return rtc::Optional<std::string>(kPayloadNameGeneric);
|
||||||
|
default:
|
||||||
|
return rtc::Optional<std::string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc::Optional<VideoCodecType> PayloadNameToCodecType(const std::string& name) {
|
||||||
|
if (name == kPayloadNameVp8)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecVP8);
|
||||||
|
if (name == kPayloadNameVp9)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecVP9);
|
||||||
|
if (name == kPayloadNameH264)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecH264);
|
||||||
|
if (name == kPayloadNameI420)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecI420);
|
||||||
|
if (name == kPayloadNameRED)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecRED);
|
||||||
|
if (name == kPayloadNameULPFEC)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecULPFEC);
|
||||||
|
if (name == kPayloadNameGeneric)
|
||||||
|
return rtc::Optional<VideoCodecType>(kVideoCodecGeneric);
|
||||||
|
return rtc::Optional<VideoCodecType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t BitrateAllocation::kMaxBitrateBps =
|
||||||
|
std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
BitrateAllocation::BitrateAllocation() : sum_(0), bitrates_{} {}
|
||||||
|
|
||||||
|
bool BitrateAllocation::SetBitrate(size_t spatial_index,
|
||||||
|
size_t temporal_index,
|
||||||
|
uint32_t bitrate_bps) {
|
||||||
|
RTC_DCHECK_LT(spatial_index, static_cast<size_t>(kMaxSpatialLayers));
|
||||||
|
RTC_DCHECK_LT(temporal_index, static_cast<size_t>(kMaxTemporalStreams));
|
||||||
|
RTC_DCHECK_LE(bitrates_[spatial_index][temporal_index], sum_);
|
||||||
|
uint64_t new_bitrate_sum_bps = sum_;
|
||||||
|
new_bitrate_sum_bps -= bitrates_[spatial_index][temporal_index];
|
||||||
|
new_bitrate_sum_bps += bitrate_bps;
|
||||||
|
if (new_bitrate_sum_bps > kMaxBitrateBps)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bitrates_[spatial_index][temporal_index] = bitrate_bps;
|
||||||
|
sum_ = static_cast<uint32_t>(new_bitrate_sum_bps);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BitrateAllocation::GetBitrate(size_t spatial_index,
|
||||||
|
size_t temporal_index) const {
|
||||||
|
RTC_DCHECK_LT(spatial_index, static_cast<size_t>(kMaxSpatialLayers));
|
||||||
|
RTC_DCHECK_LT(temporal_index, static_cast<size_t>(kMaxTemporalStreams));
|
||||||
|
return bitrates_[spatial_index][temporal_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the sum of all the temporal layer for a specific spatial layer.
|
||||||
|
uint32_t BitrateAllocation::GetSpatialLayerSum(size_t spatial_index) const {
|
||||||
|
RTC_DCHECK_LT(spatial_index, static_cast<size_t>(kMaxSpatialLayers));
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for (int i = 0; i < kMaxTemporalStreams; ++i)
|
||||||
|
sum += bitrates_[spatial_index][i];
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/optional.h"
|
||||||
#include "webrtc/common_video/rotation.h"
|
#include "webrtc/common_video/rotation.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
@ -522,7 +523,7 @@ struct VideoCodecVP8 {
|
|||||||
bool automaticResizeOn;
|
bool automaticResizeOn;
|
||||||
bool frameDroppingOn;
|
bool frameDroppingOn;
|
||||||
int keyFrameInterval;
|
int keyFrameInterval;
|
||||||
const TemporalLayersFactory* tl_factory;
|
TemporalLayersFactory* tl_factory;
|
||||||
};
|
};
|
||||||
|
|
||||||
// VP9 specific.
|
// VP9 specific.
|
||||||
@ -563,6 +564,10 @@ enum VideoCodecType {
|
|||||||
kVideoCodecUnknown
|
kVideoCodecUnknown
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Translates from name of codec to codec type and vice versa.
|
||||||
|
rtc::Optional<std::string> CodecTypeToPayloadName(VideoCodecType type);
|
||||||
|
rtc::Optional<VideoCodecType> PayloadNameToCodecType(const std::string& name);
|
||||||
|
|
||||||
union VideoCodecUnion {
|
union VideoCodecUnion {
|
||||||
VideoCodecVP8 VP8;
|
VideoCodecVP8 VP8;
|
||||||
VideoCodecVP9 VP9;
|
VideoCodecVP9 VP9;
|
||||||
@ -639,6 +644,35 @@ class VideoCodec {
|
|||||||
VideoCodecUnion codecSpecific;
|
VideoCodecUnion codecSpecific;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BitrateAllocation {
|
||||||
|
public:
|
||||||
|
static const size_t kMaxBitrateBps;
|
||||||
|
BitrateAllocation();
|
||||||
|
|
||||||
|
bool SetBitrate(size_t spatial_index,
|
||||||
|
size_t temporal_index,
|
||||||
|
uint32_t bitrate_bps);
|
||||||
|
|
||||||
|
uint32_t GetBitrate(size_t spatial_index, size_t temporal_index) const;
|
||||||
|
|
||||||
|
// Get the sum of all the temporal layer for a specific spatial layer.
|
||||||
|
uint32_t GetSpatialLayerSum(size_t spatial_index) const;
|
||||||
|
|
||||||
|
uint32_t get_sum_bps() const { return sum_; } // Sum of all bitrates.
|
||||||
|
uint32_t get_sum_kbps() const { return (sum_ + 500) / 1000; }
|
||||||
|
|
||||||
|
inline bool operator==(const BitrateAllocation& other) const {
|
||||||
|
return memcmp(bitrates_, other.bitrates_, sizeof(bitrates_)) == 0;
|
||||||
|
}
|
||||||
|
inline bool operator!=(const BitrateAllocation& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t sum_;
|
||||||
|
uint32_t bitrates_[kMaxSpatialLayers][kMaxTemporalStreams];
|
||||||
|
};
|
||||||
|
|
||||||
// Bandwidth over-use detector options. These are used to drive
|
// Bandwidth over-use detector options. These are used to drive
|
||||||
// experimentation with bandwidth estimation parameters.
|
// experimentation with bandwidth estimation parameters.
|
||||||
// See modules/remote_bitrate_estimator/overuse_detector.h
|
// See modules/remote_bitrate_estimator/overuse_detector.h
|
||||||
|
|||||||
@ -35,6 +35,7 @@ rtc_static_library("common_video") {
|
|||||||
"include/frame_callback.h",
|
"include/frame_callback.h",
|
||||||
"include/i420_buffer_pool.h",
|
"include/i420_buffer_pool.h",
|
||||||
"include/incoming_video_stream.h",
|
"include/incoming_video_stream.h",
|
||||||
|
"include/video_bitrate_allocator.h",
|
||||||
"include/video_frame_buffer.h",
|
"include/video_frame_buffer.h",
|
||||||
"incoming_video_stream.cc",
|
"incoming_video_stream.cc",
|
||||||
"libyuv/include/webrtc_libyuv.h",
|
"libyuv/include/webrtc_libyuv.h",
|
||||||
|
|||||||
@ -73,6 +73,7 @@
|
|||||||
'include/frame_callback.h',
|
'include/frame_callback.h',
|
||||||
'include/i420_buffer_pool.h',
|
'include/i420_buffer_pool.h',
|
||||||
'include/incoming_video_stream.h',
|
'include/incoming_video_stream.h',
|
||||||
|
'include/video_bitrate_allocator.h',
|
||||||
'include/video_frame_buffer.h',
|
'include/video_frame_buffer.h',
|
||||||
'libyuv/include/webrtc_libyuv.h',
|
'libyuv/include/webrtc_libyuv.h',
|
||||||
'libyuv/webrtc_libyuv.cc',
|
'libyuv/webrtc_libyuv.cc',
|
||||||
|
|||||||
30
webrtc/common_video/include/video_bitrate_allocator.h
Normal file
30
webrtc/common_video/include/video_bitrate_allocator.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_COMMON_VIDEO_INCLUDE_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
|
#define WEBRTC_COMMON_VIDEO_INCLUDE_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
|
|
||||||
|
#include "webrtc/common_types.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class VideoBitrateAllocator {
|
||||||
|
public:
|
||||||
|
VideoBitrateAllocator() {}
|
||||||
|
virtual ~VideoBitrateAllocator() {}
|
||||||
|
|
||||||
|
virtual BitrateAllocation GetAllocation(uint32_t total_bitrate,
|
||||||
|
uint32_t framerate) = 0;
|
||||||
|
virtual uint32_t GetPreferredBitrateBps(uint32_t framerate) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_COMMON_VIDEO_INCLUDE_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
@ -132,9 +132,9 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
|
|||||||
FakeWebRtcVideoEncoder()
|
FakeWebRtcVideoEncoder()
|
||||||
: init_encode_event_(false, false), num_frames_encoded_(0) {}
|
: init_encode_event_(false, false), num_frames_encoded_(0) {}
|
||||||
|
|
||||||
virtual int32_t InitEncode(const webrtc::VideoCodec* codecSettings,
|
int32_t InitEncode(const webrtc::VideoCodec* codecSettings,
|
||||||
int32_t numberOfCores,
|
int32_t numberOfCores,
|
||||||
size_t maxPayloadSize) {
|
size_t maxPayloadSize) override {
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
codec_settings_ = *codecSettings;
|
codec_settings_ = *codecSettings;
|
||||||
init_encode_event_.Set();
|
init_encode_event_.Set();
|
||||||
@ -148,27 +148,28 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
|
|||||||
return codec_settings_;
|
return codec_settings_;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int32_t Encode(const webrtc::VideoFrame& inputImage,
|
int32_t Encode(const webrtc::VideoFrame& inputImage,
|
||||||
const webrtc::CodecSpecificInfo* codecSpecificInfo,
|
const webrtc::CodecSpecificInfo* codecSpecificInfo,
|
||||||
const std::vector<webrtc::FrameType>* frame_types) {
|
const std::vector<webrtc::FrameType>* frame_types) override {
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
++num_frames_encoded_;
|
++num_frames_encoded_;
|
||||||
init_encode_event_.Set();
|
init_encode_event_.Set();
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int32_t RegisterEncodeCompleteCallback(
|
int32_t RegisterEncodeCompleteCallback(
|
||||||
webrtc::EncodedImageCallback* callback) {
|
webrtc::EncodedImageCallback* callback) override {
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int32_t Release() { return WEBRTC_VIDEO_CODEC_OK; }
|
int32_t Release() override { return WEBRTC_VIDEO_CODEC_OK; }
|
||||||
|
|
||||||
virtual int32_t SetChannelParameters(uint32_t packetLoss, int64_t rtt) {
|
int32_t SetChannelParameters(uint32_t packetLoss, int64_t rtt) override {
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) {
|
int32_t SetRateAllocation(const webrtc::BitrateAllocation& allocation,
|
||||||
|
uint32_t framerate) override {
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -507,6 +507,7 @@ if (rtc_include_tests) {
|
|||||||
"video_coding/test/stream_generator.cc",
|
"video_coding/test/stream_generator.cc",
|
||||||
"video_coding/test/stream_generator.h",
|
"video_coding/test/stream_generator.h",
|
||||||
"video_coding/timing_unittest.cc",
|
"video_coding/timing_unittest.cc",
|
||||||
|
"video_coding/utility/default_video_bitrate_allocator_unittest.cc",
|
||||||
"video_coding/utility/frame_dropper_unittest.cc",
|
"video_coding/utility/frame_dropper_unittest.cc",
|
||||||
"video_coding/utility/ivf_file_writer_unittest.cc",
|
"video_coding/utility/ivf_file_writer_unittest.cc",
|
||||||
"video_coding/utility/moving_average_unittest.cc",
|
"video_coding/utility/moving_average_unittest.cc",
|
||||||
|
|||||||
@ -33,6 +33,7 @@ rtc_static_library("video_coding") {
|
|||||||
"h264_sps_pps_tracker.h",
|
"h264_sps_pps_tracker.h",
|
||||||
"histogram.cc",
|
"histogram.cc",
|
||||||
"histogram.h",
|
"histogram.h",
|
||||||
|
"include/video_codec_initializer.h",
|
||||||
"include/video_coding.h",
|
"include/video_coding.h",
|
||||||
"include/video_coding_defines.h",
|
"include/video_coding_defines.h",
|
||||||
"inter_frame_delay.cc",
|
"inter_frame_delay.cc",
|
||||||
@ -70,6 +71,7 @@ rtc_static_library("video_coding") {
|
|||||||
"timestamp_map.h",
|
"timestamp_map.h",
|
||||||
"timing.cc",
|
"timing.cc",
|
||||||
"timing.h",
|
"timing.h",
|
||||||
|
"video_codec_initializer.cc",
|
||||||
"video_coding_impl.cc",
|
"video_coding_impl.cc",
|
||||||
"video_coding_impl.h",
|
"video_coding_impl.h",
|
||||||
"video_receiver.cc",
|
"video_receiver.cc",
|
||||||
@ -98,6 +100,8 @@ rtc_static_library("video_coding") {
|
|||||||
|
|
||||||
rtc_static_library("video_coding_utility") {
|
rtc_static_library("video_coding_utility") {
|
||||||
sources = [
|
sources = [
|
||||||
|
"utility/default_video_bitrate_allocator.cc",
|
||||||
|
"utility/default_video_bitrate_allocator.h",
|
||||||
"utility/frame_dropper.cc",
|
"utility/frame_dropper.cc",
|
||||||
"utility/frame_dropper.h",
|
"utility/frame_dropper.h",
|
||||||
"utility/ivf_file_writer.cc",
|
"utility/ivf_file_writer.cc",
|
||||||
|
|||||||
@ -152,6 +152,14 @@ static void RtpFragmentize(EncodedImage* encoded_image,
|
|||||||
|
|
||||||
H264EncoderImpl::H264EncoderImpl()
|
H264EncoderImpl::H264EncoderImpl()
|
||||||
: openh264_encoder_(nullptr),
|
: openh264_encoder_(nullptr),
|
||||||
|
width_(0),
|
||||||
|
height_(0),
|
||||||
|
max_frame_rate_(0.0f),
|
||||||
|
target_bps_(0),
|
||||||
|
max_bps_(0),
|
||||||
|
mode_(kRealtimeVideo),
|
||||||
|
frame_dropping_on_(false),
|
||||||
|
key_frame_interval_(0),
|
||||||
number_of_cores_(0),
|
number_of_cores_(0),
|
||||||
encoded_image_callback_(nullptr),
|
encoded_image_callback_(nullptr),
|
||||||
has_reported_init_(false),
|
has_reported_init_(false),
|
||||||
@ -263,11 +271,13 @@ int32_t H264EncoderImpl::RegisterEncodeCompleteCallback(
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) {
|
int32_t H264EncoderImpl::SetRateAllocation(
|
||||||
if (bitrate <= 0 || framerate <= 0) {
|
const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t framerate) {
|
||||||
|
if (bitrate_allocation.get_sum_bps() <= 0 || framerate <= 0)
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
}
|
|
||||||
target_bps_ = bitrate * 1000;
|
target_bps_ = bitrate_allocation.get_sum_bps();
|
||||||
max_frame_rate_ = static_cast<float>(framerate);
|
max_frame_rate_ = static_cast<float>(framerate);
|
||||||
quality_scaler_.ReportFramerate(framerate);
|
quality_scaler_.ReportFramerate(framerate);
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,8 @@ class H264EncoderImpl : public H264Encoder {
|
|||||||
|
|
||||||
int32_t RegisterEncodeCompleteCallback(
|
int32_t RegisterEncodeCompleteCallback(
|
||||||
EncodedImageCallback* callback) override;
|
EncodedImageCallback* callback) override;
|
||||||
int32_t SetRates(uint32_t bitrate, uint32_t framerate) override;
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t framerate) override;
|
||||||
|
|
||||||
// The result of encoding - an EncodedImage and RTPFragmentationHeader - are
|
// The result of encoding - an EncodedImage and RTPFragmentationHeader - are
|
||||||
// passed to the encode complete callback.
|
// passed to the encode complete callback.
|
||||||
@ -74,8 +75,8 @@ class H264EncoderImpl : public H264Encoder {
|
|||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
float max_frame_rate_;
|
float max_frame_rate_;
|
||||||
unsigned int target_bps_;
|
uint32_t target_bps_;
|
||||||
unsigned int max_bps_;
|
uint32_t max_bps_;
|
||||||
VideoCodecMode mode_;
|
VideoCodecMode mode_;
|
||||||
// H.264 specifc parameters
|
// H.264 specifc parameters
|
||||||
bool frame_dropping_on_;
|
bool frame_dropping_on_;
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
@ -65,10 +66,6 @@ class I420Encoder : public VideoEncoder {
|
|||||||
// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
|
// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
|
||||||
int Release() override;
|
int Release() override;
|
||||||
|
|
||||||
int SetRates(uint32_t /*newBitRate*/, uint32_t /*frameRate*/) override {
|
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SetChannelParameters(uint32_t /*packetLoss*/, int64_t /*rtt*/) override {
|
int SetChannelParameters(uint32_t /*packetLoss*/, int64_t /*rtt*/) override {
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,18 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
|
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
|
#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
#include "webrtc/system_wrappers/include/cpu_info.h"
|
#include "webrtc/system_wrappers/include/cpu_info.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -35,7 +39,7 @@ TestConfig::TestConfig()
|
|||||||
frame_length_in_bytes(0),
|
frame_length_in_bytes(0),
|
||||||
use_single_core(false),
|
use_single_core(false),
|
||||||
keyframe_interval(0),
|
keyframe_interval(0),
|
||||||
codec_settings(NULL),
|
codec_settings(nullptr),
|
||||||
verbose(true) {}
|
verbose(true) {}
|
||||||
|
|
||||||
TestConfig::~TestConfig() {}
|
TestConfig::~TestConfig() {}
|
||||||
@ -54,8 +58,9 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
|||||||
packet_manipulator_(packet_manipulator),
|
packet_manipulator_(packet_manipulator),
|
||||||
config_(config),
|
config_(config),
|
||||||
stats_(stats),
|
stats_(stats),
|
||||||
encode_callback_(NULL),
|
encode_callback_(nullptr),
|
||||||
decode_callback_(NULL),
|
decode_callback_(nullptr),
|
||||||
|
last_successful_frame_buffer_(nullptr),
|
||||||
first_key_frame_has_been_excluded_(false),
|
first_key_frame_has_been_excluded_(false),
|
||||||
last_frame_missing_(false),
|
last_frame_missing_(false),
|
||||||
initialized_(false),
|
initialized_(false),
|
||||||
@ -65,13 +70,23 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
|||||||
num_dropped_frames_(0),
|
num_dropped_frames_(0),
|
||||||
num_spatial_resizes_(0),
|
num_spatial_resizes_(0),
|
||||||
last_encoder_frame_width_(0),
|
last_encoder_frame_width_(0),
|
||||||
last_encoder_frame_height_(0) {
|
last_encoder_frame_height_(0),
|
||||||
assert(encoder);
|
bit_rate_factor_(0.0),
|
||||||
assert(decoder);
|
encode_start_ns_(0),
|
||||||
assert(frame_reader);
|
decode_start_ns_(0) {
|
||||||
assert(frame_writer);
|
std::unique_ptr<TemporalLayersFactory> tl_factory;
|
||||||
assert(packet_manipulator);
|
if (config_.codec_settings->codecType == VideoCodecType::kVideoCodecVP8) {
|
||||||
assert(stats);
|
tl_factory.reset(new TemporalLayersFactory());
|
||||||
|
config.codec_settings->VP8()->tl_factory = tl_factory.get();
|
||||||
|
}
|
||||||
|
bitrate_allocator_ = VideoCodecInitializer::CreateBitrateAllocator(
|
||||||
|
*config.codec_settings, std::move(tl_factory));
|
||||||
|
RTC_DCHECK(encoder);
|
||||||
|
RTC_DCHECK(decoder);
|
||||||
|
RTC_DCHECK(frame_reader);
|
||||||
|
RTC_DCHECK(frame_writer);
|
||||||
|
RTC_DCHECK(packet_manipulator);
|
||||||
|
RTC_DCHECK(stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoProcessorImpl::Init() {
|
bool VideoProcessorImpl::Init() {
|
||||||
@ -149,8 +164,10 @@ VideoProcessorImpl::~VideoProcessorImpl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VideoProcessorImpl::SetRates(int bit_rate, int frame_rate) {
|
void VideoProcessorImpl::SetRates(int bit_rate, int frame_rate) {
|
||||||
int set_rates_result = encoder_->SetRates(bit_rate, frame_rate);
|
int set_rates_result = encoder_->SetRateAllocation(
|
||||||
assert(set_rates_result >= 0);
|
bitrate_allocator_->GetAllocation(bit_rate * 1000, frame_rate),
|
||||||
|
frame_rate);
|
||||||
|
RTC_CHECK_GE(set_rates_result, 0);
|
||||||
if (set_rates_result < 0) {
|
if (set_rates_result < 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Failed to update encoder with new rate %d, "
|
"Failed to update encoder with new rate %d, "
|
||||||
@ -178,7 +195,7 @@ int VideoProcessorImpl::NumberSpatialResizes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
|
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
|
||||||
assert(frame_number >= 0);
|
RTC_DCHECK_GE(frame_number, 0);
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
fprintf(stderr, "Attempting to use uninitialized VideoProcessor!\n");
|
fprintf(stderr, "Attempting to use uninitialized VideoProcessor!\n");
|
||||||
return false;
|
return false;
|
||||||
@ -272,7 +289,7 @@ void VideoProcessorImpl::FrameEncoded(
|
|||||||
exclude_this_frame = true;
|
exclude_this_frame = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
RTC_NOTREACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,11 +358,11 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
|
|||||||
CalcBufferSize(kI420, up_image->width(), up_image->height());
|
CalcBufferSize(kI420, up_image->width(), up_image->height());
|
||||||
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
||||||
int extracted_length = ExtractBuffer(up_image, length, image_buffer.get());
|
int extracted_length = ExtractBuffer(up_image, length, image_buffer.get());
|
||||||
assert(extracted_length > 0);
|
RTC_DCHECK_GT(extracted_length, 0);
|
||||||
// Update our copy of the last successful frame:
|
// Update our copy of the last successful frame:
|
||||||
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
|
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
|
||||||
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
||||||
assert(write_success);
|
RTC_DCHECK(write_success);
|
||||||
if (!write_success) {
|
if (!write_success) {
|
||||||
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
||||||
}
|
}
|
||||||
@ -355,11 +372,11 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
|
|||||||
size_t length = CalcBufferSize(kI420, image.width(), image.height());
|
size_t length = CalcBufferSize(kI420, image.width(), image.height());
|
||||||
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
||||||
int extracted_length = ExtractBuffer(image, length, image_buffer.get());
|
int extracted_length = ExtractBuffer(image, length, image_buffer.get());
|
||||||
assert(extracted_length > 0);
|
RTC_DCHECK_GT(extracted_length, 0);
|
||||||
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
|
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
|
||||||
|
|
||||||
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
||||||
assert(write_success);
|
RTC_DCHECK(write_success);
|
||||||
if (!write_success) {
|
if (!write_success) {
|
||||||
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
||||||
}
|
}
|
||||||
@ -369,8 +386,8 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
|
|||||||
int VideoProcessorImpl::GetElapsedTimeMicroseconds(int64_t start,
|
int VideoProcessorImpl::GetElapsedTimeMicroseconds(int64_t start,
|
||||||
int64_t stop) {
|
int64_t stop) {
|
||||||
uint64_t encode_time = (stop - start) / rtc::kNumNanosecsPerMicrosec;
|
uint64_t encode_time = (stop - start) / rtc::kNumNanosecsPerMicrosec;
|
||||||
assert(encode_time <
|
RTC_DCHECK_LT(encode_time,
|
||||||
static_cast<unsigned int>(std::numeric_limits<int>::max()));
|
static_cast<unsigned int>(std::numeric_limits<int>::max()));
|
||||||
return static_cast<int>(encode_time);
|
return static_cast<int>(encode_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +398,7 @@ const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) {
|
|||||||
case kExcludeAllKeyFrames:
|
case kExcludeAllKeyFrames:
|
||||||
return "ExcludeAllKeyFrames";
|
return "ExcludeAllKeyFrames";
|
||||||
default:
|
default:
|
||||||
assert(false);
|
RTC_NOTREACHED();
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,7 +416,7 @@ const char* VideoCodecTypeToStr(webrtc::VideoCodecType e) {
|
|||||||
case kVideoCodecUnknown:
|
case kVideoCodecUnknown:
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
default:
|
default:
|
||||||
assert(false);
|
RTC_NOTREACHED();
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
@ -23,6 +24,9 @@
|
|||||||
#include "webrtc/video_frame.h"
|
#include "webrtc/video_frame.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
class VideoBitrateAllocator;
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
// Defines which frame types shall be excluded from packet loss and when.
|
// Defines which frame types shall be excluded from packet loss and when.
|
||||||
@ -191,6 +195,7 @@ class VideoProcessorImpl : public VideoProcessor {
|
|||||||
|
|
||||||
webrtc::VideoEncoder* encoder_;
|
webrtc::VideoEncoder* encoder_;
|
||||||
webrtc::VideoDecoder* decoder_;
|
webrtc::VideoDecoder* decoder_;
|
||||||
|
std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
|
||||||
FrameReader* frame_reader_;
|
FrameReader* frame_reader_;
|
||||||
FrameWriter* frame_writer_;
|
FrameWriter* frame_writer_;
|
||||||
PacketManipulator* packet_manipulator_;
|
PacketManipulator* packet_manipulator_;
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
|
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
|
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_coding.h"
|
#include "webrtc/modules/video_coding/include/video_coding.h"
|
||||||
@ -113,6 +114,7 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
webrtc::test::TestConfig config_;
|
webrtc::test::TestConfig config_;
|
||||||
VideoCodec codec_settings_;
|
VideoCodec codec_settings_;
|
||||||
webrtc::test::VideoProcessor* processor_;
|
webrtc::test::VideoProcessor* processor_;
|
||||||
|
TemporalLayersFactory tl_factory_;
|
||||||
|
|
||||||
// Quantities defined/updated for every encoder rate update.
|
// Quantities defined/updated for every encoder rate update.
|
||||||
// Some quantities defined per temporal layer (at most 3 layers in this test).
|
// Some quantities defined per temporal layer (at most 3 layers in this test).
|
||||||
|
|||||||
@ -13,6 +13,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/modules/include/module_common_types.h"
|
#include "webrtc/modules/include/module_common_types.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
||||||
@ -22,16 +26,17 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
DefaultTemporalLayers::DefaultTemporalLayers(int numberOfTemporalLayers,
|
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers,
|
||||||
uint8_t initial_tl0_pic_idx)
|
uint8_t initial_tl0_pic_idx)
|
||||||
: number_of_temporal_layers_(numberOfTemporalLayers),
|
: number_of_temporal_layers_(number_of_temporal_layers),
|
||||||
temporal_ids_length_(0),
|
temporal_ids_length_(0),
|
||||||
temporal_pattern_length_(0),
|
temporal_pattern_length_(0),
|
||||||
tl0_pic_idx_(initial_tl0_pic_idx),
|
tl0_pic_idx_(initial_tl0_pic_idx),
|
||||||
pattern_idx_(255),
|
pattern_idx_(255),
|
||||||
timestamp_(0),
|
timestamp_(0),
|
||||||
last_base_layer_sync_(false) {
|
last_base_layer_sync_(false) {
|
||||||
assert(kMaxTemporalStreams >= numberOfTemporalLayers);
|
RTC_CHECK_GE(kMaxTemporalStreams, number_of_temporal_layers);
|
||||||
|
RTC_CHECK_GE(number_of_temporal_layers, 0);
|
||||||
memset(temporal_ids_, 0, sizeof(temporal_ids_));
|
memset(temporal_ids_, 0, sizeof(temporal_ids_));
|
||||||
memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
|
memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
|
||||||
}
|
}
|
||||||
@ -43,18 +48,50 @@ int DefaultTemporalLayers::CurrentLayerId() const {
|
|||||||
return temporal_ids_[index];
|
return temporal_ids_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
|
std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated(
|
||||||
int max_bitrate_kbit,
|
int bitrate_kbps,
|
||||||
int framerate,
|
int max_bitrate_kbps,
|
||||||
vpx_codec_enc_cfg_t* cfg) {
|
int framerate) {
|
||||||
|
std::vector<uint32_t> bitrates;
|
||||||
|
const int num_layers = std::max(1, number_of_temporal_layers_);
|
||||||
|
for (int i = 0; i < num_layers; ++i) {
|
||||||
|
float layer_bitrate =
|
||||||
|
bitrate_kbps * kVp8LayerRateAlloction[num_layers - 1][i];
|
||||||
|
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
|
||||||
|
}
|
||||||
|
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
|
||||||
|
|
||||||
|
// Allocation table is of aggregates, transform to individual rates.
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for (int i = 0; i < num_layers; ++i) {
|
||||||
|
uint32_t layer_bitrate = bitrates[i];
|
||||||
|
RTC_DCHECK_LE(sum, bitrates[i]);
|
||||||
|
bitrates[i] -= sum;
|
||||||
|
sum = layer_bitrate;
|
||||||
|
|
||||||
|
if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
|
||||||
|
// Sum adds up; any subsequent layers will be 0.
|
||||||
|
bitrates.resize(i + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitrates;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
|
||||||
|
if (!new_bitrates_kbps_)
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (number_of_temporal_layers_) {
|
switch (number_of_temporal_layers_) {
|
||||||
case 0:
|
case 0:
|
||||||
|
FALLTHROUGH();
|
||||||
case 1:
|
case 1:
|
||||||
temporal_ids_length_ = 1;
|
temporal_ids_length_ = 1;
|
||||||
temporal_ids_[0] = 0;
|
temporal_ids_[0] = 0;
|
||||||
cfg->ts_number_layers = number_of_temporal_layers_;
|
cfg->ts_number_layers = number_of_temporal_layers_;
|
||||||
cfg->ts_periodicity = temporal_ids_length_;
|
cfg->ts_periodicity = temporal_ids_length_;
|
||||||
cfg->ts_target_bitrate[0] = bitrateKbit;
|
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
|
||||||
cfg->ts_rate_decimator[0] = 1;
|
cfg->ts_rate_decimator[0] = 1;
|
||||||
memcpy(cfg->ts_layer_id, temporal_ids_,
|
memcpy(cfg->ts_layer_id, temporal_ids_,
|
||||||
sizeof(unsigned int) * temporal_ids_length_);
|
sizeof(unsigned int) * temporal_ids_length_);
|
||||||
@ -69,8 +106,8 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
|
|||||||
cfg->ts_periodicity = temporal_ids_length_;
|
cfg->ts_periodicity = temporal_ids_length_;
|
||||||
// Split stream 60% 40%.
|
// Split stream 60% 40%.
|
||||||
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
||||||
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[1][0];
|
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
|
||||||
cfg->ts_target_bitrate[1] = bitrateKbit;
|
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
|
||||||
cfg->ts_rate_decimator[0] = 2;
|
cfg->ts_rate_decimator[0] = 2;
|
||||||
cfg->ts_rate_decimator[1] = 1;
|
cfg->ts_rate_decimator[1] = 1;
|
||||||
memcpy(cfg->ts_layer_id, temporal_ids_,
|
memcpy(cfg->ts_layer_id, temporal_ids_,
|
||||||
@ -95,9 +132,9 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
|
|||||||
cfg->ts_periodicity = temporal_ids_length_;
|
cfg->ts_periodicity = temporal_ids_length_;
|
||||||
// Split stream 40% 20% 40%.
|
// Split stream 40% 20% 40%.
|
||||||
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
||||||
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[2][0];
|
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
|
||||||
cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[2][1];
|
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
|
||||||
cfg->ts_target_bitrate[2] = bitrateKbit;
|
cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
|
||||||
cfg->ts_rate_decimator[0] = 4;
|
cfg->ts_rate_decimator[0] = 4;
|
||||||
cfg->ts_rate_decimator[1] = 2;
|
cfg->ts_rate_decimator[1] = 2;
|
||||||
cfg->ts_rate_decimator[2] = 1;
|
cfg->ts_rate_decimator[2] = 1;
|
||||||
@ -127,10 +164,10 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
|
|||||||
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
|
||||||
cfg->ts_number_layers = 4;
|
cfg->ts_number_layers = 4;
|
||||||
cfg->ts_periodicity = temporal_ids_length_;
|
cfg->ts_periodicity = temporal_ids_length_;
|
||||||
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[3][0];
|
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
|
||||||
cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[3][1];
|
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
|
||||||
cfg->ts_target_bitrate[2] = bitrateKbit * kVp8LayerRateAlloction[3][2];
|
cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
|
||||||
cfg->ts_target_bitrate[3] = bitrateKbit;
|
cfg->ts_target_bitrate[3] = (*new_bitrates_kbps_)[3];
|
||||||
cfg->ts_rate_decimator[0] = 8;
|
cfg->ts_rate_decimator[0] = 8;
|
||||||
cfg->ts_rate_decimator[1] = 4;
|
cfg->ts_rate_decimator[1] = 4;
|
||||||
cfg->ts_rate_decimator[2] = 2;
|
cfg->ts_rate_decimator[2] = 2;
|
||||||
@ -156,9 +193,12 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
|
|||||||
temporal_pattern_[15] = kTemporalUpdateNone;
|
temporal_pattern_[15] = kTemporalUpdateNone;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
RTC_NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,8 +324,18 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
|
|||||||
}
|
}
|
||||||
|
|
||||||
TemporalLayers* TemporalLayersFactory::Create(
|
TemporalLayers* TemporalLayersFactory::Create(
|
||||||
|
int simulcast_id,
|
||||||
int temporal_layers,
|
int temporal_layers,
|
||||||
uint8_t initial_tl0_pic_idx) const {
|
uint8_t initial_tl0_pic_idx) const {
|
||||||
return new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
|
TemporalLayers* tl =
|
||||||
|
new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
|
||||||
|
if (listener_)
|
||||||
|
listener_->OnTemporalLayersCreated(simulcast_id, tl);
|
||||||
|
return tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TemporalLayersFactory::SetListener(TemporalLayersListener* listener) {
|
||||||
|
listener_ = listener;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -12,8 +12,12 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
|
|
||||||
|
#include "webrtc/base/optional.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class DefaultTemporalLayers : public TemporalLayers {
|
class DefaultTemporalLayers : public TemporalLayers {
|
||||||
@ -26,10 +30,13 @@ class DefaultTemporalLayers : public TemporalLayers {
|
|||||||
// and/or update the reference buffers.
|
// and/or update the reference buffers.
|
||||||
int EncodeFlags(uint32_t timestamp) override;
|
int EncodeFlags(uint32_t timestamp) override;
|
||||||
|
|
||||||
bool ConfigureBitrates(int bitrate_kbit,
|
// Update state based on new bitrate target and incoming framerate.
|
||||||
int max_bitrate_kbit,
|
// Returns the bitrate allocation for the active temporal layers.
|
||||||
int framerate,
|
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
|
||||||
vpx_codec_enc_cfg_t* cfg) override;
|
int max_bitrate_kbps,
|
||||||
|
int framerate) override;
|
||||||
|
|
||||||
|
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
|
||||||
|
|
||||||
void PopulateCodecSpecific(bool base_layer_sync,
|
void PopulateCodecSpecific(bool base_layer_sync,
|
||||||
CodecSpecificInfoVP8* vp8_info,
|
CodecSpecificInfoVP8* vp8_info,
|
||||||
@ -37,8 +44,6 @@ class DefaultTemporalLayers : public TemporalLayers {
|
|||||||
|
|
||||||
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
|
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
|
||||||
|
|
||||||
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
|
|
||||||
|
|
||||||
int CurrentLayerId() const override;
|
int CurrentLayerId() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -77,7 +82,7 @@ class DefaultTemporalLayers : public TemporalLayers {
|
|||||||
};
|
};
|
||||||
enum { kMaxTemporalPattern = 16 };
|
enum { kMaxTemporalPattern = 16 };
|
||||||
|
|
||||||
int number_of_temporal_layers_;
|
const int number_of_temporal_layers_;
|
||||||
int temporal_ids_length_;
|
int temporal_ids_length_;
|
||||||
int temporal_ids_[kMaxTemporalPattern];
|
int temporal_ids_[kMaxTemporalPattern];
|
||||||
int temporal_pattern_length_;
|
int temporal_pattern_length_;
|
||||||
@ -86,6 +91,7 @@ class DefaultTemporalLayers : public TemporalLayers {
|
|||||||
uint8_t pattern_idx_;
|
uint8_t pattern_idx_;
|
||||||
uint32_t timestamp_;
|
uint32_t timestamp_;
|
||||||
bool last_base_layer_sync_;
|
bool last_base_layer_sync_;
|
||||||
|
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -53,7 +53,8 @@ TEST(TemporalLayersTest, 2Layers) {
|
|||||||
DefaultTemporalLayers tl(2, 0);
|
DefaultTemporalLayers tl(2, 0);
|
||||||
vpx_codec_enc_cfg_t cfg;
|
vpx_codec_enc_cfg_t cfg;
|
||||||
CodecSpecificInfoVP8 vp8_info;
|
CodecSpecificInfoVP8 vp8_info;
|
||||||
tl.ConfigureBitrates(500, 500, 30, &cfg);
|
tl.OnRatesUpdated(500, 500, 30);
|
||||||
|
tl.UpdateConfiguration(&cfg);
|
||||||
|
|
||||||
int expected_flags[16] = {
|
int expected_flags[16] = {
|
||||||
kTemporalUpdateLastAndGoldenRefAltRef,
|
kTemporalUpdateLastAndGoldenRefAltRef,
|
||||||
@ -94,7 +95,8 @@ TEST(TemporalLayersTest, 3Layers) {
|
|||||||
DefaultTemporalLayers tl(3, 0);
|
DefaultTemporalLayers tl(3, 0);
|
||||||
vpx_codec_enc_cfg_t cfg;
|
vpx_codec_enc_cfg_t cfg;
|
||||||
CodecSpecificInfoVP8 vp8_info;
|
CodecSpecificInfoVP8 vp8_info;
|
||||||
tl.ConfigureBitrates(500, 500, 30, &cfg);
|
tl.OnRatesUpdated(500, 500, 30);
|
||||||
|
tl.UpdateConfiguration(&cfg);
|
||||||
|
|
||||||
int expected_flags[16] = {
|
int expected_flags[16] = {
|
||||||
kTemporalUpdateLastAndGoldenRefAltRef,
|
kTemporalUpdateLastAndGoldenRefAltRef,
|
||||||
@ -135,7 +137,8 @@ TEST(TemporalLayersTest, 4Layers) {
|
|||||||
DefaultTemporalLayers tl(4, 0);
|
DefaultTemporalLayers tl(4, 0);
|
||||||
vpx_codec_enc_cfg_t cfg;
|
vpx_codec_enc_cfg_t cfg;
|
||||||
CodecSpecificInfoVP8 vp8_info;
|
CodecSpecificInfoVP8 vp8_info;
|
||||||
tl.ConfigureBitrates(500, 500, 30, &cfg);
|
tl.OnRatesUpdated(500, 500, 30);
|
||||||
|
tl.UpdateConfiguration(&cfg);
|
||||||
int expected_flags[16] = {
|
int expected_flags[16] = {
|
||||||
kTemporalUpdateLast,
|
kTemporalUpdateLast,
|
||||||
kTemporalUpdateNone,
|
kTemporalUpdateNone,
|
||||||
@ -175,7 +178,8 @@ TEST(TemporalLayersTest, KeyFrame) {
|
|||||||
DefaultTemporalLayers tl(3, 0);
|
DefaultTemporalLayers tl(3, 0);
|
||||||
vpx_codec_enc_cfg_t cfg;
|
vpx_codec_enc_cfg_t cfg;
|
||||||
CodecSpecificInfoVP8 vp8_info;
|
CodecSpecificInfoVP8 vp8_info;
|
||||||
tl.ConfigureBitrates(500, 500, 30, &cfg);
|
tl.OnRatesUpdated(500, 500, 30);
|
||||||
|
tl.UpdateConfiguration(&cfg);
|
||||||
|
|
||||||
int expected_flags[8] = {
|
int expected_flags[8] = {
|
||||||
kTemporalUpdateLastAndGoldenRefAltRef,
|
kTemporalUpdateLastAndGoldenRefAltRef,
|
||||||
|
|||||||
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#include "vpx/vpx_encoder.h"
|
#include "vpx/vpx_encoder.h"
|
||||||
#include "vpx/vp8cx.h"
|
#include "vpx/vp8cx.h"
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/optional.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
@ -95,26 +97,20 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
layer_ids_(NULL),
|
layer_ids_(NULL),
|
||||||
encode_flags_length_(0),
|
encode_flags_length_(0),
|
||||||
encode_flags_(NULL) {
|
encode_flags_(NULL) {
|
||||||
assert(max_temporal_layers_ >= 1);
|
RTC_CHECK_GE(max_temporal_layers_, 1);
|
||||||
assert(max_temporal_layers_ <= 3);
|
RTC_CHECK_GE(max_temporal_layers_, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~RealTimeTemporalLayers() {}
|
virtual ~RealTimeTemporalLayers() {}
|
||||||
|
|
||||||
bool ConfigureBitrates(int bitrate_kbit,
|
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
|
||||||
int max_bitrate_kbit,
|
int max_bitrate_kbps,
|
||||||
int framerate,
|
int framerate) override {
|
||||||
vpx_codec_enc_cfg_t* cfg) override {
|
|
||||||
temporal_layers_ =
|
temporal_layers_ =
|
||||||
CalculateNumberOfTemporalLayers(temporal_layers_, framerate);
|
CalculateNumberOfTemporalLayers(temporal_layers_, framerate);
|
||||||
temporal_layers_ = std::min(temporal_layers_, max_temporal_layers_);
|
temporal_layers_ = std::min(temporal_layers_, max_temporal_layers_);
|
||||||
assert(temporal_layers_ >= 1 && temporal_layers_ <= 3);
|
RTC_CHECK_GE(temporal_layers_, 1);
|
||||||
|
RTC_CHECK_LE(temporal_layers_, 3);
|
||||||
cfg->ts_number_layers = temporal_layers_;
|
|
||||||
for (int tl = 0; tl < temporal_layers_; ++tl) {
|
|
||||||
cfg->ts_target_bitrate[tl] =
|
|
||||||
bitrate_kbit * kVp8LayerRateAlloction[temporal_layers_ - 1][tl];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (temporal_layers_) {
|
switch (temporal_layers_) {
|
||||||
case 1: {
|
case 1: {
|
||||||
@ -125,9 +121,6 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
static const int encode_flags[] = {kTemporalUpdateLastRefAll};
|
static const int encode_flags[] = {kTemporalUpdateLastRefAll};
|
||||||
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
||||||
encode_flags_ = encode_flags;
|
encode_flags_ = encode_flags;
|
||||||
|
|
||||||
cfg->ts_rate_decimator[0] = 1;
|
|
||||||
cfg->ts_periodicity = layer_ids_length_;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 2: {
|
case 2: {
|
||||||
@ -146,10 +139,6 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
kTemporalUpdateNone};
|
kTemporalUpdateNone};
|
||||||
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
||||||
encode_flags_ = encode_flags;
|
encode_flags_ = encode_flags;
|
||||||
|
|
||||||
cfg->ts_rate_decimator[0] = 2;
|
|
||||||
cfg->ts_rate_decimator[1] = 1;
|
|
||||||
cfg->ts_periodicity = layer_ids_length_;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 3: {
|
case 3: {
|
||||||
@ -168,19 +157,59 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
kTemporalUpdateNone};
|
kTemporalUpdateNone};
|
||||||
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
|
||||||
encode_flags_ = encode_flags;
|
encode_flags_ = encode_flags;
|
||||||
|
|
||||||
cfg->ts_rate_decimator[0] = 4;
|
|
||||||
cfg->ts_rate_decimator[1] = 2;
|
|
||||||
cfg->ts_rate_decimator[2] = 1;
|
|
||||||
cfg->ts_periodicity = layer_ids_length_;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false);
|
RTC_NOTREACHED();
|
||||||
return false;
|
return std::vector<uint32_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> bitrates;
|
||||||
|
const int num_layers = std::max(1, temporal_layers_);
|
||||||
|
for (int i = 0; i < num_layers; ++i) {
|
||||||
|
float layer_bitrate =
|
||||||
|
bitrate_kbps * kVp8LayerRateAlloction[num_layers - 1][i];
|
||||||
|
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
|
||||||
|
}
|
||||||
|
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
|
||||||
|
|
||||||
|
// Allocation table is of aggregates, transform to individual rates.
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for (int i = 0; i < num_layers; ++i) {
|
||||||
|
uint32_t layer_bitrate = bitrates[i];
|
||||||
|
RTC_DCHECK_LE(sum, bitrates[i]);
|
||||||
|
bitrates[i] -= sum;
|
||||||
|
sum += layer_bitrate;
|
||||||
|
|
||||||
|
if (sum == static_cast<uint32_t>(bitrate_kbps)) {
|
||||||
|
// Sum adds up; any subsequent layers will be 0.
|
||||||
|
bitrates.resize(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitrates;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
|
||||||
|
if (!new_bitrates_kbps_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cfg->ts_number_layers = temporal_layers_;
|
||||||
|
for (int tl = 0; tl < temporal_layers_; ++tl) {
|
||||||
|
cfg->ts_target_bitrate[tl] = (*new_bitrates_kbps_)[tl];
|
||||||
|
}
|
||||||
|
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
|
||||||
|
|
||||||
|
cfg->ts_periodicity = layer_ids_length_;
|
||||||
|
int decimator = 1;
|
||||||
|
for (int i = temporal_layers_ - 1; i >= 0; --i, decimator *= 2) {
|
||||||
|
cfg->ts_rate_decimator[i] = decimator;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(cfg->ts_layer_id, layer_ids_,
|
memcpy(cfg->ts_layer_id, layer_ids_,
|
||||||
sizeof(unsigned int) * layer_ids_length_);
|
sizeof(unsigned int) * layer_ids_length_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,8 +277,6 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
|
|
||||||
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
|
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
|
||||||
|
|
||||||
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int temporal_layers_;
|
int temporal_layers_;
|
||||||
int max_temporal_layers_;
|
int max_temporal_layers_;
|
||||||
@ -266,12 +293,19 @@ class RealTimeTemporalLayers : public TemporalLayers {
|
|||||||
// Pattern of encode flags.
|
// Pattern of encode flags.
|
||||||
int encode_flags_length_;
|
int encode_flags_length_;
|
||||||
const int* encode_flags_;
|
const int* encode_flags_;
|
||||||
|
|
||||||
|
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TemporalLayers* RealTimeTemporalLayersFactory::Create(
|
TemporalLayers* RealTimeTemporalLayersFactory::Create(
|
||||||
|
int simulcast_id,
|
||||||
int max_temporal_layers,
|
int max_temporal_layers,
|
||||||
uint8_t initial_tl0_pic_idx) const {
|
uint8_t initial_tl0_pic_idx) const {
|
||||||
return new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
|
TemporalLayers* tl =
|
||||||
|
new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
|
||||||
|
if (listener_)
|
||||||
|
listener_->OnTemporalLayersCreated(simulcast_id, tl);
|
||||||
|
return tl;
|
||||||
}
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -65,7 +65,8 @@ ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
|
|||||||
min_qp_(-1),
|
min_qp_(-1),
|
||||||
max_qp_(-1),
|
max_qp_(-1),
|
||||||
max_debt_bytes_(0),
|
max_debt_bytes_(0),
|
||||||
frame_rate_(-1) {
|
framerate_(-1),
|
||||||
|
bitrate_updated_(false) {
|
||||||
RTC_CHECK_GT(num_temporal_layers, 0);
|
RTC_CHECK_GT(num_temporal_layers, 0);
|
||||||
RTC_CHECK_LE(num_temporal_layers, 2);
|
RTC_CHECK_LE(num_temporal_layers, 2);
|
||||||
}
|
}
|
||||||
@ -136,7 +137,7 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
|
|||||||
|
|
||||||
int64_t ts_diff;
|
int64_t ts_diff;
|
||||||
if (last_timestamp_ == -1) {
|
if (last_timestamp_ == -1) {
|
||||||
ts_diff = kOneSecond90Khz / (frame_rate_ <= 0 ? 5 : frame_rate_);
|
ts_diff = kOneSecond90Khz / (framerate_ <= 0 ? 5 : framerate_);
|
||||||
} else {
|
} else {
|
||||||
ts_diff = unwrapped_timestamp - last_timestamp_;
|
ts_diff = unwrapped_timestamp - last_timestamp_;
|
||||||
}
|
}
|
||||||
@ -147,47 +148,19 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbps,
|
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
|
||||||
int max_bitrate_kbps,
|
int max_bitrate_kbps,
|
||||||
int framerate,
|
int framerate) {
|
||||||
vpx_codec_enc_cfg_t* cfg) {
|
|
||||||
layers_[0].target_rate_kbps_ = bitrate_kbps;
|
layers_[0].target_rate_kbps_ = bitrate_kbps;
|
||||||
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
|
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
|
||||||
|
framerate_ = framerate;
|
||||||
|
bitrate_updated_ = true;
|
||||||
|
|
||||||
int target_bitrate_kbps = bitrate_kbps;
|
std::vector<uint32_t> allocation;
|
||||||
|
allocation.push_back(bitrate_kbps);
|
||||||
if (cfg != nullptr) {
|
if (max_bitrate_kbps > bitrate_kbps)
|
||||||
if (number_of_temporal_layers_ > 1) {
|
allocation.push_back(max_bitrate_kbps - bitrate_kbps);
|
||||||
// Calculate a codec target bitrate. This may be higher than TL0, gaining
|
return allocation;
|
||||||
// quality at the expense of frame rate at TL0. Constraints:
|
|
||||||
// - TL0 frame rate no less than framerate / kMaxTL0FpsReduction.
|
|
||||||
// - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
|
|
||||||
target_bitrate_kbps =
|
|
||||||
std::min(bitrate_kbps * kMaxTL0FpsReduction,
|
|
||||||
max_bitrate_kbps / kAcceptableTargetOvershoot);
|
|
||||||
|
|
||||||
cfg->rc_target_bitrate = std::max(bitrate_kbps, target_bitrate_kbps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't reconfigure qp limits during quality boost frames.
|
|
||||||
if (active_layer_ == -1 ||
|
|
||||||
layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
|
|
||||||
min_qp_ = cfg->rc_min_quantizer;
|
|
||||||
max_qp_ = cfg->rc_max_quantizer;
|
|
||||||
// After a dropped frame, a frame with max qp will be encoded and the
|
|
||||||
// quality will then ramp up from there. To boost the speed of recovery,
|
|
||||||
// encode the next frame with lower max qp. TL0 is the most important to
|
|
||||||
// improve since the errors in this layer will propagate to TL1.
|
|
||||||
// Currently, reduce max qp by 20% for TL0 and 15% for TL1.
|
|
||||||
layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
|
|
||||||
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate);
|
|
||||||
max_debt_bytes_ = 4 * avg_frame_size;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenshareLayers::FrameEncoded(unsigned int size,
|
void ScreenshareLayers::FrameEncoded(unsigned int size,
|
||||||
@ -279,9 +252,49 @@ bool ScreenshareLayers::TimeToSync(int64_t timestamp) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
|
bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
|
||||||
|
if (bitrate_updated_) {
|
||||||
|
uint32_t target_bitrate_kbps = layers_[0].target_rate_kbps_;
|
||||||
|
|
||||||
|
if (number_of_temporal_layers_ > 1) {
|
||||||
|
// Calculate a codec target bitrate. This may be higher than TL0, gaining
|
||||||
|
// quality at the expense of frame rate at TL0. Constraints:
|
||||||
|
// - TL0 frame rate no less than framerate / kMaxTL0FpsReduction.
|
||||||
|
// - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
|
||||||
|
target_bitrate_kbps =
|
||||||
|
std::min(layers_[0].target_rate_kbps_ * kMaxTL0FpsReduction,
|
||||||
|
layers_[1].target_rate_kbps_ / kAcceptableTargetOvershoot);
|
||||||
|
|
||||||
|
cfg->rc_target_bitrate =
|
||||||
|
std::max(layers_[0].target_rate_kbps_, target_bitrate_kbps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't reconfigure qp limits during quality boost frames.
|
||||||
|
if (active_layer_ == -1 ||
|
||||||
|
layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
|
||||||
|
min_qp_ = cfg->rc_min_quantizer;
|
||||||
|
max_qp_ = cfg->rc_max_quantizer;
|
||||||
|
// After a dropped frame, a frame with max qp will be encoded and the
|
||||||
|
// quality will then ramp up from there. To boost the speed of recovery,
|
||||||
|
// encode the next frame with lower max qp. TL0 is the most important to
|
||||||
|
// improve since the errors in this layer will propagate to TL1.
|
||||||
|
// Currently, reduce max qp by 20% for TL0 and 15% for TL1.
|
||||||
|
layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
|
||||||
|
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate_);
|
||||||
|
max_debt_bytes_ = 4 * avg_frame_size;
|
||||||
|
|
||||||
|
bitrate_updated_ = false;
|
||||||
|
|
||||||
|
// Don't try to update boosts state if not active yet.
|
||||||
|
if (active_layer_ == -1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTC_DCHECK_NE(-1, active_layer_);
|
||||||
if (max_qp_ == -1 || number_of_temporal_layers_ <= 1)
|
if (max_qp_ == -1 || number_of_temporal_layers_ <= 1)
|
||||||
return false;
|
return false;
|
||||||
RTC_DCHECK_NE(-1, active_layer_);
|
|
||||||
|
|
||||||
// If layer is in the quality boost state (following a dropped frame), update
|
// If layer is in the quality boost state (following a dropped frame), update
|
||||||
// the configuration with the adjusted (lower) qp and set the state back to
|
// the configuration with the adjusted (lower) qp and set the state back to
|
||||||
|
|||||||
@ -9,9 +9,7 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
|
||||||
|
|
||||||
#include <list>
|
#include <vector>
|
||||||
|
|
||||||
#include "vpx/vpx_encoder.h"
|
|
||||||
|
|
||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
@ -41,10 +39,15 @@ class ScreenshareLayers : public TemporalLayers {
|
|||||||
// and/or update the reference buffers.
|
// and/or update the reference buffers.
|
||||||
int EncodeFlags(uint32_t timestamp) override;
|
int EncodeFlags(uint32_t timestamp) override;
|
||||||
|
|
||||||
bool ConfigureBitrates(int bitrate_kbps,
|
// Update state based on new bitrate target and incoming framerate.
|
||||||
int max_bitrate_kbps,
|
// Returns the bitrate allocation for the active temporal layers.
|
||||||
int framerate,
|
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
|
||||||
vpx_codec_enc_cfg_t* cfg) override;
|
int max_bitrate_kbps,
|
||||||
|
int framerate) override;
|
||||||
|
|
||||||
|
// Update the encoder configuration with target bitrates or other parameters.
|
||||||
|
// Returns true iff the configuration was actually modified.
|
||||||
|
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
|
||||||
|
|
||||||
void PopulateCodecSpecific(bool base_layer_sync,
|
void PopulateCodecSpecific(bool base_layer_sync,
|
||||||
CodecSpecificInfoVP8* vp8_info,
|
CodecSpecificInfoVP8* vp8_info,
|
||||||
@ -54,11 +57,6 @@ class ScreenshareLayers : public TemporalLayers {
|
|||||||
|
|
||||||
int CurrentLayerId() const override;
|
int CurrentLayerId() const override;
|
||||||
|
|
||||||
// Allows the layers adapter to update the encoder configuration prior to a
|
|
||||||
// frame being encoded. Return true if the configuration should be updated
|
|
||||||
// and false if now change is needed.
|
|
||||||
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool TimeToSync(int64_t timestamp) const;
|
bool TimeToSync(int64_t timestamp) const;
|
||||||
|
|
||||||
@ -75,7 +73,8 @@ class ScreenshareLayers : public TemporalLayers {
|
|||||||
int min_qp_;
|
int min_qp_;
|
||||||
int max_qp_;
|
int max_qp_;
|
||||||
uint32_t max_debt_bytes_;
|
uint32_t max_debt_bytes_;
|
||||||
int frame_rate_;
|
int framerate_;
|
||||||
|
bool bitrate_updated_;
|
||||||
|
|
||||||
static const int kMaxNumTemporalLayers = 2;
|
static const int kMaxNumTemporalLayers = 2;
|
||||||
struct TemporalLayer {
|
struct TemporalLayer {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
|
using ::testing::ElementsAre;
|
||||||
using ::testing::NiceMock;
|
using ::testing::NiceMock;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
|
|
||||||
@ -61,8 +62,11 @@ class ScreenshareLayerTest : public ::testing::Test {
|
|||||||
memset(&vpx_cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
memset(&vpx_cfg, 0, sizeof(vpx_codec_enc_cfg_t));
|
||||||
vpx_cfg.rc_min_quantizer = min_qp_;
|
vpx_cfg.rc_min_quantizer = min_qp_;
|
||||||
vpx_cfg.rc_max_quantizer = max_qp_;
|
vpx_cfg.rc_max_quantizer = max_qp_;
|
||||||
EXPECT_TRUE(layers_->ConfigureBitrates(
|
EXPECT_THAT(layers_->OnRatesUpdated(kDefaultTl0BitrateKbps,
|
||||||
kDefaultTl0BitrateKbps, kDefaultTl1BitrateKbps, kFrameRate, &vpx_cfg));
|
kDefaultTl1BitrateKbps, kFrameRate),
|
||||||
|
ElementsAre(kDefaultTl0BitrateKbps,
|
||||||
|
kDefaultTl1BitrateKbps - kDefaultTl0BitrateKbps));
|
||||||
|
EXPECT_TRUE(layers_->UpdateConfiguration(&vpx_cfg));
|
||||||
frame_size_ = ((vpx_cfg.rc_target_bitrate * 1000) / 8) / kFrameRate;
|
frame_size_ = ((vpx_cfg.rc_target_bitrate * 1000) / 8) / kFrameRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,27 +377,41 @@ TEST_F(ScreenshareLayerTest, TooHighBitrate) {
|
|||||||
|
|
||||||
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
|
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
|
||||||
vpx_codec_enc_cfg_t cfg = GetConfig();
|
vpx_codec_enc_cfg_t cfg = GetConfig();
|
||||||
layers_->ConfigureBitrates(100, 1000, 5, &cfg);
|
const int kTl0_kbps = 100;
|
||||||
|
const int kTl1_kbps = 1000;
|
||||||
|
layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5);
|
||||||
|
|
||||||
|
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
|
||||||
|
ElementsAre(kTl0_kbps, kTl1_kbps - kTl0_kbps));
|
||||||
|
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
|
||||||
|
|
||||||
EXPECT_EQ(static_cast<unsigned int>(
|
EXPECT_EQ(static_cast<unsigned int>(
|
||||||
ScreenshareLayers::kMaxTL0FpsReduction * 100 + 0.5),
|
ScreenshareLayers::kMaxTL0FpsReduction * kTl0_kbps + 0.5),
|
||||||
cfg.rc_target_bitrate);
|
cfg.rc_target_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
|
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
|
||||||
vpx_codec_enc_cfg_t cfg = GetConfig();
|
vpx_codec_enc_cfg_t cfg = GetConfig();
|
||||||
layers_->ConfigureBitrates(100, 450, 5, &cfg);
|
const int kTl0_kbps = 100;
|
||||||
|
const int kTl1_kbps = 450;
|
||||||
|
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
|
||||||
|
ElementsAre(kTl0_kbps, kTl1_kbps - kTl0_kbps));
|
||||||
|
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
|
||||||
|
|
||||||
EXPECT_EQ(static_cast<unsigned int>(
|
EXPECT_EQ(static_cast<unsigned int>(
|
||||||
450 / ScreenshareLayers::kAcceptableTargetOvershoot),
|
kTl1_kbps / ScreenshareLayers::kAcceptableTargetOvershoot),
|
||||||
cfg.rc_target_bitrate);
|
cfg.rc_target_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
|
TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
|
||||||
vpx_codec_enc_cfg_t cfg = GetConfig();
|
vpx_codec_enc_cfg_t cfg = GetConfig();
|
||||||
layers_->ConfigureBitrates(100, 100, 5, &cfg);
|
const int kTl0_kbps = 100;
|
||||||
|
const int kTl1_kbps = 100;
|
||||||
|
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
|
||||||
|
ElementsAre(kTl0_kbps));
|
||||||
|
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
|
||||||
|
|
||||||
EXPECT_EQ(100U, cfg.rc_target_bitrate);
|
EXPECT_EQ(static_cast<uint32_t>(kTl1_kbps), cfg.rc_target_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScreenshareLayerTest, EncoderDrop) {
|
TEST_F(ScreenshareLayerTest, EncoderDrop) {
|
||||||
@ -453,7 +471,8 @@ TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
|
|||||||
const uint32_t kStartTimestamp = 1234;
|
const uint32_t kStartTimestamp = 1234;
|
||||||
|
|
||||||
vpx_codec_enc_cfg_t cfg = GetConfig();
|
vpx_codec_enc_cfg_t cfg = GetConfig();
|
||||||
layers_->ConfigureBitrates(kLowBitrateKbps, kLowBitrateKbps, 5, &cfg);
|
layers_->OnRatesUpdated(kLowBitrateKbps, kLowBitrateKbps, 5);
|
||||||
|
layers_->UpdateConfiguration(&cfg);
|
||||||
|
|
||||||
EXPECT_EQ(ScreenshareLayers::kTl0Flags,
|
EXPECT_EQ(ScreenshareLayers::kTl0Flags,
|
||||||
layers_->EncodeFlags(kStartTimestamp));
|
layers_->EncodeFlags(kStartTimestamp));
|
||||||
|
|||||||
@ -83,17 +83,6 @@ int VerifyCodec(const webrtc::VideoCodec* inst) {
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory {
|
|
||||||
ScreenshareTemporalLayersFactory() {}
|
|
||||||
virtual ~ScreenshareTemporalLayersFactory() {}
|
|
||||||
|
|
||||||
virtual webrtc::TemporalLayers* Create(int num_temporal_layers,
|
|
||||||
uint8_t initial_tl0_pic_idx) const {
|
|
||||||
return new webrtc::ScreenshareLayers(num_temporal_layers, rand(),
|
|
||||||
webrtc::Clock::GetRealTimeClock());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// An EncodedImageCallback implementation that forwards on calls to a
|
// An EncodedImageCallback implementation that forwards on calls to a
|
||||||
// SimulcastEncoderAdapter, but with the stream index it's registered with as
|
// SimulcastEncoderAdapter, but with the stream index it's registered with as
|
||||||
// the first parameter to Encoded.
|
// the first parameter to Encoded.
|
||||||
@ -116,6 +105,25 @@ class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
|
|||||||
const size_t stream_idx_;
|
const size_t stream_idx_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Utility class used to adapt the simulcast id as reported by the temporal
|
||||||
|
// layers factory, since each sub-encoder will report stream 0.
|
||||||
|
class TemporalLayersFactoryAdapter : public webrtc::TemporalLayersFactory {
|
||||||
|
public:
|
||||||
|
TemporalLayersFactoryAdapter(int adapted_simulcast_id,
|
||||||
|
const TemporalLayersFactory& tl_factory)
|
||||||
|
: adapted_simulcast_id_(adapted_simulcast_id), tl_factory_(tl_factory) {}
|
||||||
|
~TemporalLayersFactoryAdapter() override {}
|
||||||
|
webrtc::TemporalLayers* Create(int simulcast_id,
|
||||||
|
int temporal_layers,
|
||||||
|
uint8_t initial_tl0_pic_idx) const override {
|
||||||
|
return tl_factory_.Create(adapted_simulcast_id_, temporal_layers,
|
||||||
|
initial_tl0_pic_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int adapted_simulcast_id_;
|
||||||
|
const TemporalLayersFactory& tl_factory_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -125,7 +133,6 @@ SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory)
|
|||||||
encoded_complete_callback_(nullptr),
|
encoded_complete_callback_(nullptr),
|
||||||
implementation_name_("SimulcastEncoderAdapter") {
|
implementation_name_("SimulcastEncoderAdapter") {
|
||||||
memset(&codec_, 0, sizeof(webrtc::VideoCodec));
|
memset(&codec_, 0, sizeof(webrtc::VideoCodec));
|
||||||
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
|
SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
|
||||||
@ -173,14 +180,13 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
codec_ = *inst;
|
codec_ = *inst;
|
||||||
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
|
SimulcastRateAllocator rate_allocator(codec_, nullptr);
|
||||||
std::vector<uint32_t> start_bitrates =
|
BitrateAllocation allocation = rate_allocator.GetAllocation(
|
||||||
rate_allocator_->GetAllocation(codec_.startBitrate);
|
codec_.startBitrate * 1000, codec_.maxFramerate);
|
||||||
|
std::vector<uint32_t> start_bitrates;
|
||||||
// Special mode when screensharing on a single stream.
|
for (int i = 0; i < kMaxSimulcastStreams; ++i) {
|
||||||
if (number_of_streams == 1 && inst->mode == kScreensharing) {
|
uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
|
||||||
screensharing_tl_factory_.reset(new ScreenshareTemporalLayersFactory());
|
start_bitrates.push_back(stream_bitrate);
|
||||||
codec_.VP8()->tl_factory = screensharing_tl_factory_.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string implementation_name;
|
std::string implementation_name;
|
||||||
@ -200,6 +206,9 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
|
|||||||
PopulateStreamCodec(&codec_, i, start_bitrate_kbps,
|
PopulateStreamCodec(&codec_, i, start_bitrate_kbps,
|
||||||
highest_resolution_stream, &stream_codec);
|
highest_resolution_stream, &stream_codec);
|
||||||
}
|
}
|
||||||
|
TemporalLayersFactoryAdapter tl_factory_adapter(
|
||||||
|
i, *codec_.codecSpecific.VP8.tl_factory);
|
||||||
|
stream_codec.codecSpecific.VP8.tl_factory = &tl_factory_adapter;
|
||||||
|
|
||||||
// TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl.
|
// TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl.
|
||||||
if (stream_codec.qpMax < kDefaultMinQp) {
|
if (stream_codec.qpMax < kDefaultMinQp) {
|
||||||
@ -340,61 +349,48 @@ int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SimulcastEncoderAdapter::SetRates(uint32_t new_bitrate_kbit,
|
int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
uint32_t new_framerate) {
|
uint32_t new_framerate) {
|
||||||
if (!Initialized()) {
|
if (!Initialized())
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
}
|
|
||||||
if (new_framerate < 1) {
|
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
||||||
}
|
|
||||||
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
|
|
||||||
new_bitrate_kbit = codec_.maxBitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint32_t> stream_bitrates;
|
if (new_framerate < 1)
|
||||||
if (new_bitrate_kbit > 0) {
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
|
|
||||||
|
if (codec_.maxBitrate > 0 && bitrate.get_sum_kbps() > codec_.maxBitrate)
|
||||||
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
|
|
||||||
|
if (bitrate.get_sum_bps() > 0) {
|
||||||
// Make sure the bitrate fits the configured min bitrates. 0 is a special
|
// Make sure the bitrate fits the configured min bitrates. 0 is a special
|
||||||
// value that means paused, though, so leave it alone.
|
// value that means paused, though, so leave it alone.
|
||||||
if (new_bitrate_kbit < codec_.minBitrate) {
|
if (bitrate.get_sum_kbps() < codec_.minBitrate)
|
||||||
new_bitrate_kbit = codec_.minBitrate;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
}
|
|
||||||
if (codec_.numberOfSimulcastStreams > 0 &&
|
if (codec_.numberOfSimulcastStreams > 0 &&
|
||||||
new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
|
bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) {
|
||||||
new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
}
|
}
|
||||||
stream_bitrates = rate_allocator_->GetAllocation(new_bitrate_kbit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codec_.maxFramerate = new_framerate;
|
codec_.maxFramerate = new_framerate;
|
||||||
|
|
||||||
// Disable any stream not in the current allocation.
|
|
||||||
stream_bitrates.resize(streaminfos_.size(), 0U);
|
|
||||||
|
|
||||||
for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
||||||
uint32_t stream_bitrate_kbps = stream_bitrates[stream_idx];
|
uint32_t stream_bitrate_kbps =
|
||||||
|
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
|
||||||
|
|
||||||
// Need a key frame if we have not sent this stream before.
|
// Need a key frame if we have not sent this stream before.
|
||||||
if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
|
if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
|
||||||
streaminfos_[stream_idx].key_frame_request = true;
|
streaminfos_[stream_idx].key_frame_request = true;
|
||||||
}
|
}
|
||||||
streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
|
streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
|
||||||
|
|
||||||
// TODO(holmer): This is a temporary hack for screensharing, where we
|
// Slice the temporal layers out of the full allocation and pass it on to
|
||||||
// interpret the startBitrate as the encoder target bitrate. This is
|
// the encoder handling the current simulcast stream.
|
||||||
// to allow for a different max bitrate, so if the codec can't meet
|
BitrateAllocation stream_allocation;
|
||||||
// the target we still allow it to overshoot up to the max before dropping
|
for (int i = 0; i < kMaxTemporalStreams; ++i)
|
||||||
// frames. This hack should be improved.
|
stream_allocation.SetBitrate(0, i, bitrate.GetBitrate(stream_idx, i));
|
||||||
if (codec_.targetBitrate > 0 &&
|
streaminfos_[stream_idx].encoder->SetRateAllocation(stream_allocation,
|
||||||
(codec_.VP8()->numberOfTemporalLayers == 2 ||
|
new_framerate);
|
||||||
codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
|
|
||||||
stream_bitrate_kbps = std::min(codec_.maxBitrate, stream_bitrate_kbps);
|
|
||||||
// TODO(ronghuawu): Can't change max bitrate via the VideoEncoder
|
|
||||||
// interface. And VP8EncoderImpl doesn't take negative framerate.
|
|
||||||
// max_bitrate = std::min(codec_.maxBitrate, stream_bitrate_kbps);
|
|
||||||
// new_framerate = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
streaminfos_[stream_idx].encoder->SetRates(stream_bitrate_kbps,
|
|
||||||
new_framerate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
|
|||||||
@ -48,7 +48,8 @@ class SimulcastEncoderAdapter : public VP8Encoder {
|
|||||||
const std::vector<FrameType>* frame_types) override;
|
const std::vector<FrameType>* frame_types) override;
|
||||||
int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
|
int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
|
||||||
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
||||||
int SetRates(uint32_t new_bitrate_kbit, uint32_t new_framerate) override;
|
int SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
|
uint32_t new_framerate) override;
|
||||||
|
|
||||||
// Eventual handler for the contained encoders' EncodedImageCallbacks, but
|
// Eventual handler for the contained encoders' EncodedImageCallbacks, but
|
||||||
// called from an internal helper that also knows the correct stream
|
// called from an internal helper that also knows the correct stream
|
||||||
@ -103,12 +104,10 @@ class SimulcastEncoderAdapter : public VP8Encoder {
|
|||||||
bool Initialized() const;
|
bool Initialized() const;
|
||||||
|
|
||||||
std::unique_ptr<VideoEncoderFactory> factory_;
|
std::unique_ptr<VideoEncoderFactory> factory_;
|
||||||
std::unique_ptr<TemporalLayersFactory> screensharing_tl_factory_;
|
|
||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
std::vector<StreamInfo> streaminfos_;
|
std::vector<StreamInfo> streaminfos_;
|
||||||
EncodedImageCallback* encoded_complete_callback_;
|
EncodedImageCallback* encoded_complete_callback_;
|
||||||
std::string implementation_name_;
|
std::string implementation_name_;
|
||||||
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -95,12 +95,6 @@ TEST_F(TestSimulcastEncoderAdapter, TestSpatioTemporalLayers321PatternEncoder) {
|
|||||||
TestVp8Simulcast::TestSpatioTemporalLayers321PatternEncoder();
|
TestVp8Simulcast::TestSpatioTemporalLayers321PatternEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ronghuawu): Enable this test when SkipEncodingUnusedStreams option is
|
|
||||||
// implemented for SimulcastEncoderAdapter.
|
|
||||||
TEST_F(TestSimulcastEncoderAdapter, DISABLED_TestSkipEncodingUnusedStreams) {
|
|
||||||
TestVp8Simulcast::TestSkipEncodingUnusedStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TestSimulcastEncoderAdapter, DISABLED_TestRPSIEncoder) {
|
TEST_F(TestSimulcastEncoderAdapter, DISABLED_TestRPSIEncoder) {
|
||||||
TestVp8Simulcast::TestRPSIEncoder();
|
TestVp8Simulcast::TestRPSIEncoder();
|
||||||
}
|
}
|
||||||
@ -132,8 +126,9 @@ class MockVideoEncoder : public VideoEncoder {
|
|||||||
|
|
||||||
int32_t Release() /* override */ { return 0; }
|
int32_t Release() /* override */ { return 0; }
|
||||||
|
|
||||||
int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) /* override */ {
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
|
||||||
last_set_bitrate_ = static_cast<int32_t>(newBitRate);
|
uint32_t framerate) {
|
||||||
|
last_set_bitrate_ = bitrate_allocation;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,13 +155,13 @@ class MockVideoEncoder : public VideoEncoder {
|
|||||||
void set_supports_native_handle(bool enabled) {
|
void set_supports_native_handle(bool enabled) {
|
||||||
supports_native_handle_ = enabled;
|
supports_native_handle_ = enabled;
|
||||||
}
|
}
|
||||||
int32_t last_set_bitrate() const { return last_set_bitrate_; }
|
BitrateAllocation last_set_bitrate() const { return last_set_bitrate_; }
|
||||||
|
|
||||||
MOCK_CONST_METHOD0(ImplementationName, const char*());
|
MOCK_CONST_METHOD0(ImplementationName, const char*());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool supports_native_handle_ = false;
|
bool supports_native_handle_ = false;
|
||||||
int32_t last_set_bitrate_ = -1;
|
BitrateAllocation last_set_bitrate_;
|
||||||
|
|
||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
EncodedImageCallback* callback_;
|
EncodedImageCallback* callback_;
|
||||||
@ -273,6 +268,9 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
|
|||||||
void SetupCodec() {
|
void SetupCodec() {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
rate_allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
|
||||||
|
tl_factory_.SetListener(rate_allocator_.get());
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
||||||
adapter_->RegisterEncodeCompleteCallback(this);
|
adapter_->RegisterEncodeCompleteCallback(this);
|
||||||
}
|
}
|
||||||
@ -301,7 +299,6 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
|
|||||||
EXPECT_EQ(ref.VP8().automaticResizeOn, target.VP8().automaticResizeOn);
|
EXPECT_EQ(ref.VP8().automaticResizeOn, target.VP8().automaticResizeOn);
|
||||||
EXPECT_EQ(ref.VP8().frameDroppingOn, target.VP8().frameDroppingOn);
|
EXPECT_EQ(ref.VP8().frameDroppingOn, target.VP8().frameDroppingOn);
|
||||||
EXPECT_EQ(ref.VP8().keyFrameInterval, target.VP8().keyFrameInterval);
|
EXPECT_EQ(ref.VP8().keyFrameInterval, target.VP8().keyFrameInterval);
|
||||||
EXPECT_EQ(ref.VP8().tl_factory, target.VP8().tl_factory);
|
|
||||||
EXPECT_EQ(ref.qpMax, target.qpMax);
|
EXPECT_EQ(ref.qpMax, target.qpMax);
|
||||||
EXPECT_EQ(0, target.numberOfSimulcastStreams);
|
EXPECT_EQ(0, target.numberOfSimulcastStreams);
|
||||||
EXPECT_EQ(ref.mode, target.mode);
|
EXPECT_EQ(ref.mode, target.mode);
|
||||||
@ -314,6 +311,7 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
|
|||||||
*ref_codec = codec_;
|
*ref_codec = codec_;
|
||||||
ref_codec->VP8()->numberOfTemporalLayers =
|
ref_codec->VP8()->numberOfTemporalLayers =
|
||||||
kTestTemporalLayerProfile[stream_index];
|
kTestTemporalLayerProfile[stream_index];
|
||||||
|
ref_codec->codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
ref_codec->width = codec_.simulcastStream[stream_index].width;
|
ref_codec->width = codec_.simulcastStream[stream_index].width;
|
||||||
ref_codec->height = codec_.simulcastStream[stream_index].height;
|
ref_codec->height = codec_.simulcastStream[stream_index].height;
|
||||||
ref_codec->maxBitrate = codec_.simulcastStream[stream_index].maxBitrate;
|
ref_codec->maxBitrate = codec_.simulcastStream[stream_index].maxBitrate;
|
||||||
@ -357,6 +355,8 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
|
|||||||
int last_encoded_image_width_;
|
int last_encoded_image_width_;
|
||||||
int last_encoded_image_height_;
|
int last_encoded_image_height_;
|
||||||
int last_encoded_image_simulcast_index_;
|
int last_encoded_image_simulcast_index_;
|
||||||
|
TemporalLayersFactory tl_factory_;
|
||||||
|
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) {
|
TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) {
|
||||||
@ -376,7 +376,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) {
|
|||||||
SetupCodec();
|
SetupCodec();
|
||||||
|
|
||||||
// Set bitrates so that we send all layers.
|
// Set bitrates so that we send all layers.
|
||||||
adapter_->SetRates(1200, 30);
|
adapter_->SetRateAllocation(rate_allocator_->GetAllocation(1200, 30), 30);
|
||||||
|
|
||||||
// At this point, the simulcast encoder adapter should have 3 streams: HD,
|
// At this point, the simulcast encoder adapter should have 3 streams: HD,
|
||||||
// quarter HD, and quarter quarter HD. We're going to mostly ignore the exact
|
// quarter HD, and quarter quarter HD. We're going to mostly ignore the exact
|
||||||
@ -409,6 +409,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) {
|
|||||||
TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) {
|
TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
codec_.numberOfSimulcastStreams = 1;
|
codec_.numberOfSimulcastStreams = 1;
|
||||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
||||||
adapter_->RegisterEncodeCompleteCallback(this);
|
adapter_->RegisterEncodeCompleteCallback(this);
|
||||||
@ -422,27 +423,37 @@ TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) {
|
|||||||
TEST_F(TestSimulcastEncoderAdapterFake, SetRatesUnderMinBitrate) {
|
TEST_F(TestSimulcastEncoderAdapterFake, SetRatesUnderMinBitrate) {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
codec_.minBitrate = 50;
|
codec_.minBitrate = 50;
|
||||||
codec_.numberOfSimulcastStreams = 1;
|
codec_.numberOfSimulcastStreams = 1;
|
||||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
||||||
|
rate_allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
|
||||||
|
|
||||||
// Above min should be respected.
|
// Above min should be respected.
|
||||||
adapter_->SetRates(100, 30);
|
BitrateAllocation target_bitrate =
|
||||||
EXPECT_EQ(100, helper_->factory()->encoders()[0]->last_set_bitrate());
|
rate_allocator_->GetAllocation(codec_.minBitrate * 1000, 30);
|
||||||
|
adapter_->SetRateAllocation(target_bitrate, 30);
|
||||||
|
EXPECT_EQ(target_bitrate,
|
||||||
|
helper_->factory()->encoders()[0]->last_set_bitrate());
|
||||||
|
|
||||||
// Below min but non-zero should be replaced with the min bitrate.
|
// Below min but non-zero should be replaced with the min bitrate.
|
||||||
adapter_->SetRates(15, 30);
|
BitrateAllocation too_low_bitrate =
|
||||||
EXPECT_EQ(50, helper_->factory()->encoders()[0]->last_set_bitrate());
|
rate_allocator_->GetAllocation((codec_.minBitrate - 1) * 1000, 30);
|
||||||
|
adapter_->SetRateAllocation(too_low_bitrate, 30);
|
||||||
|
EXPECT_EQ(target_bitrate,
|
||||||
|
helper_->factory()->encoders()[0]->last_set_bitrate());
|
||||||
|
|
||||||
// Zero should be passed on as is, since it means "pause".
|
// Zero should be passed on as is, since it means "pause".
|
||||||
adapter_->SetRates(0, 30);
|
adapter_->SetRateAllocation(BitrateAllocation(), 30);
|
||||||
EXPECT_EQ(0, helper_->factory()->encoders()[0]->last_set_bitrate());
|
EXPECT_EQ(BitrateAllocation(),
|
||||||
|
helper_->factory()->encoders()[0]->last_set_bitrate());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) {
|
TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) {
|
||||||
EXPECT_STREQ("SimulcastEncoderAdapter", adapter_->ImplementationName());
|
EXPECT_STREQ("SimulcastEncoderAdapter", adapter_->ImplementationName());
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
std::vector<const char*> encoder_names;
|
std::vector<const char*> encoder_names;
|
||||||
encoder_names.push_back("codec1");
|
encoder_names.push_back("codec1");
|
||||||
encoder_names.push_back("codec2");
|
encoder_names.push_back("codec2");
|
||||||
@ -465,6 +476,7 @@ TEST_F(TestSimulcastEncoderAdapterFake,
|
|||||||
SupportsNativeHandleForMultipleStreams) {
|
SupportsNativeHandleForMultipleStreams) {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
codec_.numberOfSimulcastStreams = 3;
|
codec_.numberOfSimulcastStreams = 3;
|
||||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
||||||
adapter_->RegisterEncodeCompleteCallback(this);
|
adapter_->RegisterEncodeCompleteCallback(this);
|
||||||
@ -493,6 +505,7 @@ TEST_F(TestSimulcastEncoderAdapterFake,
|
|||||||
NativeHandleForwardingForMultipleStreams) {
|
NativeHandleForwardingForMultipleStreams) {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
codec_.numberOfSimulcastStreams = 3;
|
codec_.numberOfSimulcastStreams = 3;
|
||||||
// High start bitrate, so all streams are enabled.
|
// High start bitrate, so all streams are enabled.
|
||||||
codec_.startBitrate = 3000;
|
codec_.startBitrate = 3000;
|
||||||
@ -517,6 +530,7 @@ TEST_F(TestSimulcastEncoderAdapterFake,
|
|||||||
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) {
|
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) {
|
||||||
TestVp8Simulcast::DefaultSettings(
|
TestVp8Simulcast::DefaultSettings(
|
||||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
|
||||||
codec_.numberOfSimulcastStreams = 3;
|
codec_.numberOfSimulcastStreams = 3;
|
||||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
|
||||||
adapter_->RegisterEncodeCompleteCallback(this);
|
adapter_->RegisterEncodeCompleteCallback(this);
|
||||||
|
|||||||
@ -86,10 +86,5 @@ TEST_F(TestVp8Impl, TestSpatioTemporalLayers321PatternEncoder) {
|
|||||||
TEST_F(TestVp8Impl, TestStrideEncodeDecode) {
|
TEST_F(TestVp8Impl, TestStrideEncodeDecode) {
|
||||||
TestVp8Simulcast::TestStrideEncodeDecode();
|
TestVp8Simulcast::TestStrideEncodeDecode();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestVp8Impl, TestSkipEncodingUnusedStreams) {
|
|
||||||
TestVp8Simulcast::TestSkipEncodingUnusedStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -20,6 +21,7 @@
|
|||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/include/mock/mock_video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/mock/mock_video_codec_interface.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
#include "webrtc/video_frame.h"
|
#include "webrtc/video_frame.h"
|
||||||
|
|
||||||
@ -147,83 +149,6 @@ class Vp8TestDecodedImageCallback : public DecodedImageCallback {
|
|||||||
int decoded_frames_;
|
int decoded_frames_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SkipEncodingUnusedStreamsTest {
|
|
||||||
public:
|
|
||||||
std::vector<unsigned int> RunTest(VP8Encoder* encoder,
|
|
||||||
VideoCodec* settings,
|
|
||||||
uint32_t target_bitrate) {
|
|
||||||
SpyingTemporalLayersFactory spy_factory;
|
|
||||||
settings->VP8()->tl_factory = &spy_factory;
|
|
||||||
EXPECT_EQ(0, encoder->InitEncode(settings, 1, 1200));
|
|
||||||
|
|
||||||
encoder->SetRates(target_bitrate, 30);
|
|
||||||
|
|
||||||
std::vector<unsigned int> configured_bitrates;
|
|
||||||
for (std::vector<TemporalLayers*>::const_iterator it =
|
|
||||||
spy_factory.spying_layers_.begin();
|
|
||||||
it != spy_factory.spying_layers_.end(); ++it) {
|
|
||||||
configured_bitrates.push_back(
|
|
||||||
static_cast<SpyingTemporalLayers*>(*it)->configured_bitrate_);
|
|
||||||
}
|
|
||||||
return configured_bitrates;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpyingTemporalLayers : public TemporalLayers {
|
|
||||||
public:
|
|
||||||
explicit SpyingTemporalLayers(TemporalLayers* layers)
|
|
||||||
: configured_bitrate_(0), layers_(layers) {}
|
|
||||||
|
|
||||||
virtual ~SpyingTemporalLayers() { delete layers_; }
|
|
||||||
|
|
||||||
int EncodeFlags(uint32_t timestamp) override {
|
|
||||||
return layers_->EncodeFlags(timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConfigureBitrates(int bitrate_kbit,
|
|
||||||
int max_bitrate_kbit,
|
|
||||||
int framerate,
|
|
||||||
vpx_codec_enc_cfg_t* cfg) override {
|
|
||||||
configured_bitrate_ = bitrate_kbit;
|
|
||||||
return layers_->ConfigureBitrates(bitrate_kbit, max_bitrate_kbit,
|
|
||||||
framerate, cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PopulateCodecSpecific(bool base_layer_sync,
|
|
||||||
CodecSpecificInfoVP8* vp8_info,
|
|
||||||
uint32_t timestamp) override {
|
|
||||||
layers_->PopulateCodecSpecific(base_layer_sync, vp8_info, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {
|
|
||||||
layers_->FrameEncoded(size, timestamp, qp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CurrentLayerId() const override { return layers_->CurrentLayerId(); }
|
|
||||||
|
|
||||||
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int configured_bitrate_;
|
|
||||||
TemporalLayers* layers_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SpyingTemporalLayersFactory : public TemporalLayersFactory {
|
|
||||||
public:
|
|
||||||
virtual ~SpyingTemporalLayersFactory() {}
|
|
||||||
TemporalLayers* Create(int temporal_layers,
|
|
||||||
uint8_t initial_tl0_pic_idx) const override {
|
|
||||||
SpyingTemporalLayers* layers =
|
|
||||||
new SpyingTemporalLayers(TemporalLayersFactory::Create(
|
|
||||||
temporal_layers, initial_tl0_pic_idx));
|
|
||||||
spying_layers_.push_back(layers);
|
|
||||||
return layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutable std::vector<TemporalLayers*> spying_layers_;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestVp8Simulcast : public ::testing::Test {
|
class TestVp8Simulcast : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
TestVp8Simulcast(VP8Encoder* encoder, VP8Decoder* decoder)
|
TestVp8Simulcast(VP8Encoder* encoder, VP8Decoder* decoder)
|
||||||
@ -265,7 +190,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
static void DefaultSettings(VideoCodec* settings,
|
static void DefaultSettings(VideoCodec* settings,
|
||||||
const int* temporal_layer_profile) {
|
const int* temporal_layer_profile) {
|
||||||
assert(settings);
|
RTC_CHECK(settings);
|
||||||
memset(settings, 0, sizeof(VideoCodec));
|
memset(settings, 0, sizeof(VideoCodec));
|
||||||
strncpy(settings->plName, "VP8", 4);
|
strncpy(settings->plName, "VP8", 4);
|
||||||
settings->codecType = kVideoCodecVP8;
|
settings->codecType = kVideoCodecVP8;
|
||||||
@ -315,12 +240,18 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void SetUp() { SetUpCodec(kDefaultTemporalLayerProfile); }
|
void SetUp() override { SetUpCodec(kDefaultTemporalLayerProfile); }
|
||||||
|
|
||||||
virtual void SetUpCodec(const int* temporal_layer_profile) {
|
void TearDown() override {
|
||||||
|
encoder_->Release();
|
||||||
|
decoder_->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUpCodec(const int* temporal_layer_profile) {
|
||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback_);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback_);
|
||||||
decoder_->RegisterDecodeCompleteCallback(&decoder_callback_);
|
decoder_->RegisterDecodeCompleteCallback(&decoder_callback_);
|
||||||
DefaultSettings(&settings_, temporal_layer_profile);
|
DefaultSettings(&settings_, temporal_layer_profile);
|
||||||
|
SetUpRateAllocator();
|
||||||
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
||||||
EXPECT_EQ(0, decoder_->InitDecode(&settings_, 1));
|
EXPECT_EQ(0, decoder_->InitDecode(&settings_, 1));
|
||||||
int half_width = (kDefaultWidth + 1) / 2;
|
int half_width = (kDefaultWidth + 1) / 2;
|
||||||
@ -331,9 +262,16 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
new VideoFrame(input_buffer_, 0, 0, webrtc::kVideoRotation_0));
|
new VideoFrame(input_buffer_, 0, 0, webrtc::kVideoRotation_0));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown() {
|
void SetUpRateAllocator() {
|
||||||
encoder_->Release();
|
TemporalLayersFactory* tl_factory = new TemporalLayersFactory();
|
||||||
decoder_->Release();
|
rate_allocator_.reset(new SimulcastRateAllocator(
|
||||||
|
settings_, std::unique_ptr<TemporalLayersFactory>(tl_factory)));
|
||||||
|
settings_.codecSpecific.VP8.tl_factory = tl_factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRates(uint32_t bitrate_kbps, uint32_t fps) {
|
||||||
|
encoder_->SetRateAllocation(
|
||||||
|
rate_allocator_->GetAllocation(bitrate_kbps * 1000, fps), fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpectStreams(FrameType frame_type, int expected_video_streams) {
|
void ExpectStreams(FrameType frame_type, int expected_video_streams) {
|
||||||
@ -396,7 +334,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
// We currently expect all active streams to generate a key frame even though
|
// We currently expect all active streams to generate a key frame even though
|
||||||
// a key frame was only requested for some of them.
|
// a key frame was only requested for some of them.
|
||||||
void TestKeyFrameRequestsOnAllStreams() {
|
void TestKeyFrameRequestsOnAllStreams() {
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, kNumberOfSimulcastStreams);
|
ExpectStreams(kVideoFrameKey, kNumberOfSimulcastStreams);
|
||||||
@ -431,7 +369,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
void TestPaddingAllStreams() {
|
void TestPaddingAllStreams() {
|
||||||
// We should always encode the base layer.
|
// We should always encode the base layer.
|
||||||
encoder_->SetRates(kMinBitrates[0] - 1, 30);
|
SetRates(kMinBitrates[0] - 1, 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 1);
|
ExpectStreams(kVideoFrameKey, 1);
|
||||||
@ -444,7 +382,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
void TestPaddingTwoStreams() {
|
void TestPaddingTwoStreams() {
|
||||||
// We have just enough to get only the first stream and padding for two.
|
// We have just enough to get only the first stream and padding for two.
|
||||||
encoder_->SetRates(kMinBitrates[0], 30);
|
SetRates(kMinBitrates[0], 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 1);
|
ExpectStreams(kVideoFrameKey, 1);
|
||||||
@ -458,7 +396,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
void TestPaddingTwoStreamsOneMaxedOut() {
|
void TestPaddingTwoStreamsOneMaxedOut() {
|
||||||
// We are just below limit of sending second stream, so we should get
|
// We are just below limit of sending second stream, so we should get
|
||||||
// the first stream maxed out (at |maxBitrate|), and padding for two.
|
// the first stream maxed out (at |maxBitrate|), and padding for two.
|
||||||
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
|
SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 1);
|
ExpectStreams(kVideoFrameKey, 1);
|
||||||
@ -471,7 +409,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
void TestPaddingOneStream() {
|
void TestPaddingOneStream() {
|
||||||
// We have just enough to send two streams, so padding for one stream.
|
// We have just enough to send two streams, so padding for one stream.
|
||||||
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
|
SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 2);
|
ExpectStreams(kVideoFrameKey, 2);
|
||||||
@ -485,8 +423,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
void TestPaddingOneStreamTwoMaxedOut() {
|
void TestPaddingOneStreamTwoMaxedOut() {
|
||||||
// We are just below limit of sending third stream, so we should get
|
// We are just below limit of sending third stream, so we should get
|
||||||
// first stream's rate maxed out at |targetBitrate|, second at |maxBitrate|.
|
// first stream's rate maxed out at |targetBitrate|, second at |maxBitrate|.
|
||||||
encoder_->SetRates(
|
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
|
||||||
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
|
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 2);
|
ExpectStreams(kVideoFrameKey, 2);
|
||||||
@ -499,8 +436,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
void TestSendAllStreams() {
|
void TestSendAllStreams() {
|
||||||
// We have just enough to send all streams.
|
// We have just enough to send all streams.
|
||||||
encoder_->SetRates(
|
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
|
||||||
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
|
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 3);
|
ExpectStreams(kVideoFrameKey, 3);
|
||||||
@ -513,7 +449,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
|
|
||||||
void TestDisablingStreams() {
|
void TestDisablingStreams() {
|
||||||
// We should get three media streams.
|
// We should get three media streams.
|
||||||
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
|
SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
ExpectStreams(kVideoFrameKey, 3);
|
ExpectStreams(kVideoFrameKey, 3);
|
||||||
@ -524,36 +460,33 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
||||||
|
|
||||||
// We should only get two streams and padding for one.
|
// We should only get two streams and padding for one.
|
||||||
encoder_->SetRates(
|
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
|
||||||
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
|
|
||||||
ExpectStreams(kVideoFrameDelta, 2);
|
ExpectStreams(kVideoFrameDelta, 2);
|
||||||
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
||||||
|
|
||||||
// We should only get the first stream and padding for two.
|
// We should only get the first stream and padding for two.
|
||||||
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
|
SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
|
||||||
ExpectStreams(kVideoFrameDelta, 1);
|
ExpectStreams(kVideoFrameDelta, 1);
|
||||||
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
||||||
|
|
||||||
// We don't have enough bitrate for the thumbnail stream, but we should get
|
// We don't have enough bitrate for the thumbnail stream, but we should get
|
||||||
// it anyway with current configuration.
|
// it anyway with current configuration.
|
||||||
encoder_->SetRates(kTargetBitrates[0] - 1, 30);
|
SetRates(kTargetBitrates[0] - 1, 30);
|
||||||
ExpectStreams(kVideoFrameDelta, 1);
|
ExpectStreams(kVideoFrameDelta, 1);
|
||||||
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
||||||
|
|
||||||
// We should only get two streams and padding for one.
|
// We should only get two streams and padding for one.
|
||||||
encoder_->SetRates(
|
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
|
||||||
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
|
|
||||||
// We get a key frame because a new stream is being enabled.
|
// We get a key frame because a new stream is being enabled.
|
||||||
ExpectStreams(kVideoFrameKey, 2);
|
ExpectStreams(kVideoFrameKey, 2);
|
||||||
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
|
||||||
|
|
||||||
// We should get all three streams.
|
// We should get all three streams.
|
||||||
encoder_->SetRates(
|
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kTargetBitrates[2], 30);
|
||||||
kTargetBitrates[0] + kTargetBitrates[1] + kTargetBitrates[2], 30);
|
|
||||||
// We get a key frame because a new stream is being enabled.
|
// We get a key frame because a new stream is being enabled.
|
||||||
ExpectStreams(kVideoFrameKey, 3);
|
ExpectStreams(kVideoFrameKey, 3);
|
||||||
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
|
||||||
@ -590,10 +523,11 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
settings_.width;
|
settings_.width;
|
||||||
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].height =
|
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].height =
|
||||||
settings_.height;
|
settings_.height;
|
||||||
|
SetUpRateAllocator();
|
||||||
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
||||||
|
|
||||||
// Encode one frame and verify.
|
// Encode one frame and verify.
|
||||||
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
|
SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
|
||||||
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
|
||||||
kVideoFrameDelta);
|
kVideoFrameDelta);
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
@ -611,8 +545,9 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
DefaultSettings(&settings_, kDefaultTemporalLayerProfile);
|
DefaultSettings(&settings_, kDefaultTemporalLayerProfile);
|
||||||
// Start at the lowest bitrate for enabling base stream.
|
// Start at the lowest bitrate for enabling base stream.
|
||||||
settings_.startBitrate = kMinBitrates[0];
|
settings_.startBitrate = kMinBitrates[0];
|
||||||
|
SetUpRateAllocator();
|
||||||
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
|
||||||
encoder_->SetRates(settings_.startBitrate, 30);
|
SetRates(settings_.startBitrate, 30);
|
||||||
ExpectStreams(kVideoFrameKey, 1);
|
ExpectStreams(kVideoFrameKey, 1);
|
||||||
// Resize |input_frame_| to the new resolution.
|
// Resize |input_frame_| to the new resolution.
|
||||||
half_width = (settings_.width + 1) / 2;
|
half_width = (settings_.width + 1) / 2;
|
||||||
@ -634,7 +569,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
Vp8TestEncodedImageCallback encoder_callback;
|
Vp8TestEncodedImageCallback encoder_callback;
|
||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
||||||
|
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
|
|
||||||
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, NULL));
|
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, NULL));
|
||||||
int picture_id = -1;
|
int picture_id = -1;
|
||||||
@ -703,7 +638,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
||||||
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
|
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
|
||||||
|
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
|
|
||||||
// Set color.
|
// Set color.
|
||||||
int plane_offset[kNumOfPlanes];
|
int plane_offset[kNumOfPlanes];
|
||||||
@ -777,7 +712,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
void TestSaptioTemporalLayers333PatternEncoder() {
|
void TestSaptioTemporalLayers333PatternEncoder() {
|
||||||
Vp8TestEncodedImageCallback encoder_callback;
|
Vp8TestEncodedImageCallback encoder_callback;
|
||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
|
|
||||||
int expected_temporal_idx[3] = {-1, -1, -1};
|
int expected_temporal_idx[3] = {-1, -1, -1};
|
||||||
bool expected_layer_sync[3] = {false, false, false};
|
bool expected_layer_sync[3] = {false, false, false};
|
||||||
@ -846,7 +781,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
SetUpCodec(temporal_layer_profile);
|
SetUpCodec(temporal_layer_profile);
|
||||||
Vp8TestEncodedImageCallback encoder_callback;
|
Vp8TestEncodedImageCallback encoder_callback;
|
||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
|
|
||||||
int expected_temporal_idx[3] = {-1, -1, -1};
|
int expected_temporal_idx[3] = {-1, -1, -1};
|
||||||
bool expected_layer_sync[3] = {false, false, false};
|
bool expected_layer_sync[3] = {false, false, false};
|
||||||
@ -905,7 +840,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
|
||||||
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
|
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
|
||||||
|
|
||||||
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
SetRates(kMaxBitrates[2], 30); // To get all three streams.
|
||||||
// Setting two (possibly) problematic use cases for stride:
|
// Setting two (possibly) problematic use cases for stride:
|
||||||
// 1. stride > width 2. stride_y != stride_uv/2
|
// 1. stride > width 2. stride_y != stride_uv/2
|
||||||
int stride_y = kDefaultWidth + 20;
|
int stride_y = kDefaultWidth + 20;
|
||||||
@ -941,30 +876,6 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
EXPECT_EQ(2, decoder_callback.DecodedFrames());
|
EXPECT_EQ(2, decoder_callback.DecodedFrames());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSkipEncodingUnusedStreams() {
|
|
||||||
SkipEncodingUnusedStreamsTest test;
|
|
||||||
std::vector<unsigned int> configured_bitrate =
|
|
||||||
test.RunTest(encoder_.get(), &settings_,
|
|
||||||
1); // Target bit rate 1, to force all streams but the
|
|
||||||
// base one to be exceeding bandwidth constraints.
|
|
||||||
EXPECT_EQ(static_cast<size_t>(kNumberOfSimulcastStreams),
|
|
||||||
configured_bitrate.size());
|
|
||||||
|
|
||||||
unsigned int min_bitrate =
|
|
||||||
std::max(settings_.simulcastStream[0].minBitrate, settings_.minBitrate);
|
|
||||||
int stream = 0;
|
|
||||||
for (std::vector<unsigned int>::const_iterator it =
|
|
||||||
configured_bitrate.begin();
|
|
||||||
it != configured_bitrate.end(); ++it) {
|
|
||||||
if (stream == 0) {
|
|
||||||
EXPECT_EQ(min_bitrate, *it);
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(0u, *it);
|
|
||||||
}
|
|
||||||
++stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<VP8Encoder> encoder_;
|
std::unique_ptr<VP8Encoder> encoder_;
|
||||||
MockEncodedImageCallback encoder_callback_;
|
MockEncodedImageCallback encoder_callback_;
|
||||||
std::unique_ptr<VP8Decoder> decoder_;
|
std::unique_ptr<VP8Decoder> decoder_;
|
||||||
@ -972,6 +883,7 @@ class TestVp8Simulcast : public ::testing::Test {
|
|||||||
VideoCodec settings_;
|
VideoCodec settings_;
|
||||||
rtc::scoped_refptr<I420Buffer> input_buffer_;
|
rtc::scoped_refptr<I420Buffer> input_buffer_;
|
||||||
std::unique_ptr<VideoFrame> input_frame_;
|
std::unique_ptr<VideoFrame> input_frame_;
|
||||||
|
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|||||||
@ -12,18 +12,21 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||||
|
|
||||||
#include "vpx/vpx_encoder.h"
|
#include <vector>
|
||||||
|
|
||||||
#include "webrtc/common_video/include/video_image.h"
|
#include "webrtc/common_video/include/video_image.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
|
struct vpx_codec_enc_cfg;
|
||||||
|
typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t;
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
struct CodecSpecificInfoVP8;
|
struct CodecSpecificInfoVP8;
|
||||||
|
|
||||||
class TemporalLayers {
|
class TemporalLayers {
|
||||||
public:
|
public:
|
||||||
// Factory for TemporalLayer strategy. Default behaviour is a fixed pattern
|
// Factory for TemporalLayer strategy. Default behavior is a fixed pattern
|
||||||
// of temporal layers. See default_temporal_layers.cc
|
// of temporal layers. See default_temporal_layers.cc
|
||||||
virtual ~TemporalLayers() {}
|
virtual ~TemporalLayers() {}
|
||||||
|
|
||||||
@ -31,10 +34,15 @@ class TemporalLayers {
|
|||||||
// and/or update the reference buffers.
|
// and/or update the reference buffers.
|
||||||
virtual int EncodeFlags(uint32_t timestamp) = 0;
|
virtual int EncodeFlags(uint32_t timestamp) = 0;
|
||||||
|
|
||||||
virtual bool ConfigureBitrates(int bitrate_kbit,
|
// Update state based on new bitrate target and incoming framerate.
|
||||||
int max_bitrate_kbit,
|
// Returns the bitrate allocation for the active temporal layers.
|
||||||
int framerate,
|
virtual std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
|
||||||
vpx_codec_enc_cfg_t* cfg) = 0;
|
int max_bitrate_kbps,
|
||||||
|
int framerate) = 0;
|
||||||
|
|
||||||
|
// Update the encoder configuration with target bitrates or other parameters.
|
||||||
|
// Returns true iff the configuration was actually modified.
|
||||||
|
virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
|
||||||
|
|
||||||
virtual void PopulateCodecSpecific(bool base_layer_sync,
|
virtual void PopulateCodecSpecific(bool base_layer_sync,
|
||||||
CodecSpecificInfoVP8* vp8_info,
|
CodecSpecificInfoVP8* vp8_info,
|
||||||
@ -43,26 +51,42 @@ class TemporalLayers {
|
|||||||
virtual void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) = 0;
|
virtual void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) = 0;
|
||||||
|
|
||||||
virtual int CurrentLayerId() const = 0;
|
virtual int CurrentLayerId() const = 0;
|
||||||
|
|
||||||
virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TemporalLayersListener;
|
||||||
class TemporalLayersFactory {
|
class TemporalLayersFactory {
|
||||||
public:
|
public:
|
||||||
|
TemporalLayersFactory() : listener_(nullptr) {}
|
||||||
virtual ~TemporalLayersFactory() {}
|
virtual ~TemporalLayersFactory() {}
|
||||||
virtual TemporalLayers* Create(int temporal_layers,
|
virtual TemporalLayers* Create(int simulcast_id,
|
||||||
|
int temporal_layers,
|
||||||
uint8_t initial_tl0_pic_idx) const;
|
uint8_t initial_tl0_pic_idx) const;
|
||||||
|
void SetListener(TemporalLayersListener* listener);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TemporalLayersListener* listener_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Factory for a temporal layers strategy that adaptively changes the number of
|
// Factory for a temporal layers strategy that adaptively changes the number of
|
||||||
// layers based on input framerate so that the base layer has an acceptable
|
// layers based on input frame rate so that the base layer has an acceptable
|
||||||
// framerate. See realtime_temporal_layers.cc
|
// frame rate. See realtime_temporal_layers.cc
|
||||||
class RealTimeTemporalLayersFactory : public TemporalLayersFactory {
|
class RealTimeTemporalLayersFactory : public TemporalLayersFactory {
|
||||||
public:
|
public:
|
||||||
|
RealTimeTemporalLayersFactory() {}
|
||||||
~RealTimeTemporalLayersFactory() override {}
|
~RealTimeTemporalLayersFactory() override {}
|
||||||
TemporalLayers* Create(int num_temporal_layers,
|
TemporalLayers* Create(int simulcast_id,
|
||||||
|
int num_temporal_layers,
|
||||||
uint8_t initial_tl0_pic_idx) const override;
|
uint8_t initial_tl0_pic_idx) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TemporalLayersListener {
|
||||||
|
public:
|
||||||
|
TemporalLayersListener() {}
|
||||||
|
virtual ~TemporalLayersListener() {}
|
||||||
|
|
||||||
|
virtual void OnTemporalLayersCreated(int simulcast_id,
|
||||||
|
TemporalLayers* layers) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/test/frame_utils.h"
|
#include "webrtc/test/frame_utils.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
#include "webrtc/test/testsupport/fileutils.h"
|
#include "webrtc/test/testsupport/fileutils.h"
|
||||||
@ -161,6 +162,8 @@ class TestVp8Impl : public ::testing::Test {
|
|||||||
codec_inst_.maxBitrate = 4000;
|
codec_inst_.maxBitrate = 4000;
|
||||||
codec_inst_.qpMax = 56;
|
codec_inst_.qpMax = 56;
|
||||||
codec_inst_.VP8()->denoisingOn = true;
|
codec_inst_.VP8()->denoisingOn = true;
|
||||||
|
codec_inst_.VP8()->tl_factory = &tl_factory_;
|
||||||
|
codec_inst_.VP8()->numberOfTemporalLayers = 1;
|
||||||
|
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||||
encoder_->InitEncode(&codec_inst_, 1, 1440));
|
encoder_->InitEncode(&codec_inst_, 1, 1440));
|
||||||
@ -201,6 +204,7 @@ class TestVp8Impl : public ::testing::Test {
|
|||||||
EncodedImage encoded_frame_;
|
EncodedImage encoded_frame_;
|
||||||
VideoFrame decoded_frame_;
|
VideoFrame decoded_frame_;
|
||||||
VideoCodec codec_inst_;
|
VideoCodec codec_inst_;
|
||||||
|
TemporalLayersFactory tl_factory_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(TestVp8Impl, EncoderParameterTest) {
|
TEST_F(TestVp8Impl, EncoderParameterTest) {
|
||||||
@ -215,11 +219,15 @@ TEST_F(TestVp8Impl, EncoderParameterTest) {
|
|||||||
codec_inst_.qpMax = 56;
|
codec_inst_.qpMax = 56;
|
||||||
codec_inst_.VP8()->complexity = kComplexityNormal;
|
codec_inst_.VP8()->complexity = kComplexityNormal;
|
||||||
codec_inst_.VP8()->numberOfTemporalLayers = 1;
|
codec_inst_.VP8()->numberOfTemporalLayers = 1;
|
||||||
|
codec_inst_.VP8()->tl_factory = &tl_factory_;
|
||||||
// Calls before InitEncode().
|
// Calls before InitEncode().
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
|
||||||
int bit_rate = 300;
|
int bit_rate = 300;
|
||||||
|
BitrateAllocation bitrate_allocation;
|
||||||
|
bitrate_allocation.SetBitrate(0, 0, bit_rate * 1000);
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED,
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED,
|
||||||
encoder_->SetRates(bit_rate, codec_inst_.maxFramerate));
|
encoder_->SetRateAllocation(bitrate_allocation,
|
||||||
|
codec_inst_.maxFramerate));
|
||||||
|
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_inst_, 1, 1440));
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_inst_, 1, 1440));
|
||||||
|
|
||||||
|
|||||||
@ -113,7 +113,6 @@ VP8Decoder* VP8Decoder::Create() {
|
|||||||
|
|
||||||
VP8EncoderImpl::VP8EncoderImpl()
|
VP8EncoderImpl::VP8EncoderImpl()
|
||||||
: encoded_complete_callback_(nullptr),
|
: encoded_complete_callback_(nullptr),
|
||||||
rate_allocator_(new SimulcastRateAllocator(codec_)),
|
|
||||||
inited_(false),
|
inited_(false),
|
||||||
timestamp_(0),
|
timestamp_(0),
|
||||||
feedback_mode_(false),
|
feedback_mode_(false),
|
||||||
@ -175,27 +174,32 @@ int VP8EncoderImpl::Release() {
|
|||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
int VP8EncoderImpl::SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
uint32_t new_framerate) {
|
uint32_t new_framerate) {
|
||||||
if (!inited_) {
|
if (!inited_)
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
}
|
|
||||||
if (encoders_[0].err) {
|
if (encoders_[0].err)
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
|
||||||
if (new_framerate < 1) {
|
if (new_framerate < 1)
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
|
|
||||||
|
if (bitrate.get_sum_bps() == 0) {
|
||||||
|
// Encoder paused, turn off all encoding.
|
||||||
|
const int num_streams = static_cast<size_t>(encoders_.size());
|
||||||
|
for (int i = 0; i < num_streams; ++i)
|
||||||
|
SetStreamState(false, i);
|
||||||
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
|
|
||||||
new_bitrate_kbit = codec_.maxBitrate;
|
// At this point, bitrate allocation should already match codec settings.
|
||||||
}
|
if (codec_.maxBitrate > 0)
|
||||||
if (new_bitrate_kbit < codec_.minBitrate) {
|
RTC_DCHECK_LE(bitrate.get_sum_kbps(), codec_.maxBitrate);
|
||||||
new_bitrate_kbit = codec_.minBitrate;
|
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.minBitrate);
|
||||||
}
|
if (codec_.numberOfSimulcastStreams > 0)
|
||||||
if (codec_.numberOfSimulcastStreams > 0 &&
|
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.simulcastStream[0].minBitrate);
|
||||||
new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
|
|
||||||
new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
|
|
||||||
}
|
|
||||||
codec_.maxFramerate = new_framerate;
|
codec_.maxFramerate = new_framerate;
|
||||||
|
|
||||||
if (encoders_.size() == 1) {
|
if (encoders_.size() == 1) {
|
||||||
@ -207,14 +211,14 @@ int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
|||||||
// Only trigger keyframes if we are allowed to scale down.
|
// Only trigger keyframes if we are allowed to scale down.
|
||||||
if (configurations_[0].rc_resize_allowed) {
|
if (configurations_[0].rc_resize_allowed) {
|
||||||
if (!down_scale_requested_) {
|
if (!down_scale_requested_) {
|
||||||
if (k_pixels_per_frame > new_bitrate_kbit) {
|
if (k_pixels_per_frame > bitrate.get_sum_kbps()) {
|
||||||
down_scale_requested_ = true;
|
down_scale_requested_ = true;
|
||||||
down_scale_bitrate_ = new_bitrate_kbit;
|
down_scale_bitrate_ = bitrate.get_sum_kbps();
|
||||||
key_frame_request_[0] = true;
|
key_frame_request_[0] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (new_bitrate_kbit > (2 * down_scale_bitrate_) ||
|
if (bitrate.get_sum_kbps() > (2 * down_scale_bitrate_) ||
|
||||||
new_bitrate_kbit < (down_scale_bitrate_ / 2)) {
|
bitrate.get_sum_kbps() < (down_scale_bitrate_ / 2)) {
|
||||||
down_scale_requested_ = false;
|
down_scale_requested_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,31 +237,18 @@ int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> stream_bitrates =
|
|
||||||
rate_allocator_->GetAllocation(new_bitrate_kbit);
|
|
||||||
size_t stream_idx = encoders_.size() - 1;
|
size_t stream_idx = encoders_.size() - 1;
|
||||||
for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
|
for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
|
||||||
if (encoders_.size() > 1)
|
unsigned int target_bitrate_kbps =
|
||||||
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
|
||||||
|
|
||||||
|
bool send_stream = target_bitrate_kbps > 0;
|
||||||
|
if (send_stream || encoders_.size() > 1)
|
||||||
|
SetStreamState(send_stream, stream_idx);
|
||||||
|
|
||||||
|
configurations_[i].rc_target_bitrate = target_bitrate_kbps;
|
||||||
|
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[i]);
|
||||||
|
|
||||||
unsigned int target_bitrate = stream_bitrates[stream_idx];
|
|
||||||
unsigned int max_bitrate = codec_.maxBitrate;
|
|
||||||
int framerate = new_framerate;
|
|
||||||
// TODO(holmer): This is a temporary hack for screensharing, where we
|
|
||||||
// interpret the startBitrate as the encoder target bitrate. This is
|
|
||||||
// to allow for a different max bitrate, so if the codec can't meet
|
|
||||||
// the target we still allow it to overshoot up to the max before dropping
|
|
||||||
// frames. This hack should be improved.
|
|
||||||
if (codec_.targetBitrate > 0 &&
|
|
||||||
(codec_.VP8()->numberOfTemporalLayers == 2 ||
|
|
||||||
codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
|
|
||||||
int tl0_bitrate = std::min(codec_.targetBitrate, target_bitrate);
|
|
||||||
max_bitrate = std::min(codec_.maxBitrate, target_bitrate);
|
|
||||||
target_bitrate = tl0_bitrate;
|
|
||||||
}
|
|
||||||
configurations_[i].rc_target_bitrate = target_bitrate;
|
|
||||||
temporal_layers_[stream_idx]->ConfigureBitrates(
|
|
||||||
target_bitrate, max_bitrate, framerate, &configurations_[i]);
|
|
||||||
if (vpx_codec_enc_config_set(&encoders_[i], &configurations_[i])) {
|
if (vpx_codec_enc_config_set(&encoders_[i], &configurations_[i])) {
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
@ -287,26 +278,17 @@ void VP8EncoderImpl::SetStreamState(bool send_stream,
|
|||||||
void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
|
void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
|
||||||
int num_temporal_layers,
|
int num_temporal_layers,
|
||||||
const VideoCodec& codec) {
|
const VideoCodec& codec) {
|
||||||
TemporalLayersFactory default_factory;
|
RTC_DCHECK(codec.codecSpecific.VP8.tl_factory != nullptr);
|
||||||
const TemporalLayersFactory* tl_factory = codec.VP8().tl_factory;
|
const TemporalLayersFactory* tl_factory = codec.codecSpecific.VP8.tl_factory;
|
||||||
if (!tl_factory)
|
|
||||||
tl_factory = &default_factory;
|
|
||||||
if (num_streams == 1) {
|
if (num_streams == 1) {
|
||||||
if (codec.mode == kScreensharing) {
|
temporal_layers_.push_back(
|
||||||
// Special mode when screensharing on a single stream.
|
tl_factory->Create(0, num_temporal_layers, rand()));
|
||||||
temporal_layers_.push_back(new ScreenshareLayers(
|
|
||||||
num_temporal_layers, rand(), webrtc::Clock::GetRealTimeClock()));
|
|
||||||
} else {
|
|
||||||
temporal_layers_.push_back(
|
|
||||||
tl_factory->Create(num_temporal_layers, rand()));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < num_streams; ++i) {
|
for (int i = 0; i < num_streams; ++i) {
|
||||||
// TODO(andresp): crash if layers is invalid.
|
RTC_CHECK_GT(num_temporal_layers, 0);
|
||||||
int layers = codec.simulcastStream[i].numberOfTemporalLayers;
|
int layers = std::max(static_cast<uint8_t>(1),
|
||||||
if (layers < 1)
|
codec.simulcastStream[i].numberOfTemporalLayers);
|
||||||
layers = 1;
|
temporal_layers_.push_back(tl_factory->Create(i, layers, rand()));
|
||||||
temporal_layers_.push_back(tl_factory->Create(layers, rand()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,10 +333,8 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
|
|||||||
int num_temporal_layers =
|
int num_temporal_layers =
|
||||||
doing_simulcast ? inst->simulcastStream[0].numberOfTemporalLayers
|
doing_simulcast ? inst->simulcastStream[0].numberOfTemporalLayers
|
||||||
: inst->VP8().numberOfTemporalLayers;
|
: inst->VP8().numberOfTemporalLayers;
|
||||||
|
RTC_DCHECK_GT(num_temporal_layers, 0);
|
||||||
|
|
||||||
// TODO(andresp): crash if num temporal layers is bananas.
|
|
||||||
if (num_temporal_layers < 1)
|
|
||||||
num_temporal_layers = 1;
|
|
||||||
SetupTemporalLayers(number_of_streams, num_temporal_layers, *inst);
|
SetupTemporalLayers(number_of_streams, num_temporal_layers, *inst);
|
||||||
|
|
||||||
feedback_mode_ = inst->VP8().feedbackModeOn;
|
feedback_mode_ = inst->VP8().feedbackModeOn;
|
||||||
@ -362,7 +342,6 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
|
|||||||
number_of_cores_ = number_of_cores;
|
number_of_cores_ = number_of_cores;
|
||||||
timestamp_ = 0;
|
timestamp_ = 0;
|
||||||
codec_ = *inst;
|
codec_ = *inst;
|
||||||
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
|
|
||||||
|
|
||||||
// Code expects simulcastStream resolutions to be correct, make sure they are
|
// Code expects simulcastStream resolutions to be correct, make sure they are
|
||||||
// filled even when there are no simulcast layers.
|
// filled even when there are no simulcast layers.
|
||||||
@ -514,45 +493,44 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
|
|||||||
vpx_img_wrap(&raw_images_[0], VPX_IMG_FMT_I420, inst->width, inst->height, 1,
|
vpx_img_wrap(&raw_images_[0], VPX_IMG_FMT_I420, inst->width, inst->height, 1,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (encoders_.size() == 1) {
|
// Note the order we use is different from webm, we have lowest resolution
|
||||||
configurations_[0].rc_target_bitrate = inst->startBitrate;
|
// at position 0 and they have highest resolution at position 0.
|
||||||
temporal_layers_[0]->ConfigureBitrates(inst->startBitrate, inst->maxBitrate,
|
int stream_idx = encoders_.size() - 1;
|
||||||
inst->maxFramerate,
|
SimulcastRateAllocator init_allocator(codec_, nullptr);
|
||||||
&configurations_[0]);
|
BitrateAllocation allocation = init_allocator.GetAllocation(
|
||||||
} else {
|
inst->startBitrate * 1000, inst->maxFramerate);
|
||||||
// Note the order we use is different from webm, we have lowest resolution
|
std::vector<uint32_t> stream_bitrates;
|
||||||
// at position 0 and they have highest resolution at position 0.
|
for (int i = 0; i == 0 || i < inst->numberOfSimulcastStreams; ++i) {
|
||||||
int stream_idx = encoders_.size() - 1;
|
uint32_t bitrate = allocation.GetSpatialLayerSum(i) / 1000;
|
||||||
std::vector<uint32_t> stream_bitrates =
|
stream_bitrates.push_back(bitrate);
|
||||||
rate_allocator_->GetAllocation(inst->startBitrate);
|
}
|
||||||
|
|
||||||
|
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
|
||||||
|
temporal_layers_[stream_idx]->OnRatesUpdated(
|
||||||
|
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate);
|
||||||
|
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[0]);
|
||||||
|
--stream_idx;
|
||||||
|
for (size_t i = 1; i < encoders_.size(); ++i, --stream_idx) {
|
||||||
|
memcpy(&configurations_[i], &configurations_[0],
|
||||||
|
sizeof(configurations_[0]));
|
||||||
|
|
||||||
|
configurations_[i].g_w = inst->simulcastStream[stream_idx].width;
|
||||||
|
configurations_[i].g_h = inst->simulcastStream[stream_idx].height;
|
||||||
|
|
||||||
|
// Use 1 thread for lower resolutions.
|
||||||
|
configurations_[i].g_threads = 1;
|
||||||
|
|
||||||
|
// Setting alignment to 32 - as that ensures at least 16 for all
|
||||||
|
// planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
|
||||||
|
// the y plane, but only half of it to the u and v planes.
|
||||||
|
vpx_img_alloc(&raw_images_[i], VPX_IMG_FMT_I420,
|
||||||
|
inst->simulcastStream[stream_idx].width,
|
||||||
|
inst->simulcastStream[stream_idx].height, kVp832ByteAlign);
|
||||||
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
||||||
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
|
configurations_[i].rc_target_bitrate = stream_bitrates[stream_idx];
|
||||||
temporal_layers_[stream_idx]->ConfigureBitrates(
|
temporal_layers_[stream_idx]->OnRatesUpdated(
|
||||||
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate,
|
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate);
|
||||||
&configurations_[0]);
|
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[i]);
|
||||||
--stream_idx;
|
|
||||||
for (size_t i = 1; i < encoders_.size(); ++i, --stream_idx) {
|
|
||||||
memcpy(&configurations_[i], &configurations_[0],
|
|
||||||
sizeof(configurations_[0]));
|
|
||||||
|
|
||||||
configurations_[i].g_w = inst->simulcastStream[stream_idx].width;
|
|
||||||
configurations_[i].g_h = inst->simulcastStream[stream_idx].height;
|
|
||||||
|
|
||||||
// Use 1 thread for lower resolutions.
|
|
||||||
configurations_[i].g_threads = 1;
|
|
||||||
|
|
||||||
// Setting alignment to 32 - as that ensures at least 16 for all
|
|
||||||
// planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
|
|
||||||
// the y plane, but only half of it to the u and v planes.
|
|
||||||
vpx_img_alloc(&raw_images_[i], VPX_IMG_FMT_I420,
|
|
||||||
inst->simulcastStream[stream_idx].width,
|
|
||||||
inst->simulcastStream[stream_idx].height, kVp832ByteAlign);
|
|
||||||
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
|
||||||
configurations_[i].rc_target_bitrate = stream_bitrates[stream_idx];
|
|
||||||
temporal_layers_[stream_idx]->ConfigureBitrates(
|
|
||||||
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate,
|
|
||||||
&configurations_[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rps_.Init();
|
rps_.Init();
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class SimulcastRateAllocator;
|
|
||||||
class TemporalLayers;
|
class TemporalLayers;
|
||||||
|
|
||||||
class VP8EncoderImpl : public VP8Encoder {
|
class VP8EncoderImpl : public VP8Encoder {
|
||||||
@ -55,7 +54,8 @@ class VP8EncoderImpl : public VP8Encoder {
|
|||||||
|
|
||||||
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
||||||
|
|
||||||
int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
|
int SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
|
uint32_t new_framerate) override;
|
||||||
|
|
||||||
void OnDroppedFrame() override;
|
void OnDroppedFrame() override;
|
||||||
|
|
||||||
@ -94,7 +94,6 @@ class VP8EncoderImpl : public VP8Encoder {
|
|||||||
|
|
||||||
EncodedImageCallback* encoded_complete_callback_;
|
EncodedImageCallback* encoded_complete_callback_;
|
||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
|
||||||
bool inited_;
|
bool inited_;
|
||||||
int64_t timestamp_;
|
int64_t timestamp_;
|
||||||
bool feedback_mode_;
|
bool feedback_mode_;
|
||||||
|
|||||||
@ -77,6 +77,7 @@ VP9EncoderImpl::VP9EncoderImpl()
|
|||||||
frames_since_kf_(0),
|
frames_since_kf_(0),
|
||||||
num_temporal_layers_(0),
|
num_temporal_layers_(0),
|
||||||
num_spatial_layers_(0),
|
num_spatial_layers_(0),
|
||||||
|
is_flexible_mode_(false),
|
||||||
frames_encoded_(0),
|
frames_encoded_(0),
|
||||||
// Use two spatial when screensharing with flexible mode.
|
// Use two spatial when screensharing with flexible mode.
|
||||||
spatial_layer_(new ScreenshareLayersVP9(2)) {
|
spatial_layer_(new ScreenshareLayersVP9(2)) {
|
||||||
@ -193,24 +194,28 @@ bool VP9EncoderImpl::SetSvcRates() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
int VP9EncoderImpl::SetRateAllocation(
|
||||||
uint32_t new_framerate) {
|
const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t frame_rate) {
|
||||||
if (!inited_) {
|
if (!inited_) {
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
}
|
}
|
||||||
if (encoder_->err) {
|
if (encoder_->err) {
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
if (new_framerate < 1) {
|
if (frame_rate < 1) {
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
}
|
}
|
||||||
// Update bit rate
|
// Update bit rate
|
||||||
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
|
if (codec_.maxBitrate > 0 &&
|
||||||
new_bitrate_kbit = codec_.maxBitrate;
|
bitrate_allocation.get_sum_kbps() > codec_.maxBitrate) {
|
||||||
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
}
|
}
|
||||||
config_->rc_target_bitrate = new_bitrate_kbit;
|
|
||||||
codec_.maxFramerate = new_framerate;
|
// TODO(sprang): Actually use BitrateAllocation layer info.
|
||||||
spatial_layer_->ConfigureBitrate(new_bitrate_kbit, 0);
|
config_->rc_target_bitrate = bitrate_allocation.get_sum_kbps();
|
||||||
|
codec_.maxFramerate = frame_rate;
|
||||||
|
spatial_layer_->ConfigureBitrate(bitrate_allocation.get_sum_kbps(), 0);
|
||||||
|
|
||||||
if (!SetSvcRates()) {
|
if (!SetSvcRates()) {
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
|
|||||||
@ -46,7 +46,8 @@ class VP9EncoderImpl : public VP9Encoder {
|
|||||||
|
|
||||||
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
||||||
|
|
||||||
int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
|
int SetRateAllocation(const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t frame_rate) override;
|
||||||
|
|
||||||
void OnDroppedFrame() override {}
|
void OnDroppedFrame() override {}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ VCMGenericEncoder::VCMGenericEncoder(
|
|||||||
: encoder_(encoder),
|
: encoder_(encoder),
|
||||||
vcm_encoded_frame_callback_(encoded_frame_callback),
|
vcm_encoded_frame_callback_(encoded_frame_callback),
|
||||||
internal_source_(internal_source),
|
internal_source_(internal_source),
|
||||||
encoder_params_({0, 0, 0, 0}),
|
encoder_params_({BitrateAllocation(), 0, 0, 0}),
|
||||||
is_screenshare_(false) {}
|
is_screenshare_(false) {}
|
||||||
|
|
||||||
VCMGenericEncoder::~VCMGenericEncoder() {}
|
VCMGenericEncoder::~VCMGenericEncoder() {}
|
||||||
@ -90,11 +90,23 @@ void VCMGenericEncoder::SetEncoderParameters(const EncoderParameters& params) {
|
|||||||
params.input_frame_rate != encoder_params_.input_frame_rate;
|
params.input_frame_rate != encoder_params_.input_frame_rate;
|
||||||
encoder_params_ = params;
|
encoder_params_ = params;
|
||||||
}
|
}
|
||||||
if (channel_parameters_have_changed)
|
if (channel_parameters_have_changed) {
|
||||||
encoder_->SetChannelParameters(params.loss_rate, params.rtt);
|
int res = encoder_->SetChannelParameters(params.loss_rate, params.rtt);
|
||||||
|
if (res != 0) {
|
||||||
|
LOG(LS_WARNING) << "Error set encoder parameters (loss = "
|
||||||
|
<< params.loss_rate << ", rtt = " << params.rtt
|
||||||
|
<< "): " << res;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (rates_have_changed) {
|
if (rates_have_changed) {
|
||||||
uint32_t target_bitrate_kbps = (params.target_bitrate + 500) / 1000;
|
int res = encoder_->SetRateAllocation(params.target_bitrate,
|
||||||
encoder_->SetRates(target_bitrate_kbps, params.input_frame_rate);
|
params.input_frame_rate);
|
||||||
|
if (res != 0) {
|
||||||
|
LOG(LS_WARNING) << "Error set encoder rate (total bitrate bps = "
|
||||||
|
<< params.target_bitrate.get_sum_bps()
|
||||||
|
<< ", framerate = " << params.input_frame_rate
|
||||||
|
<< "): " << res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ class MediaOptimization;
|
|||||||
} // namespace media_optimization
|
} // namespace media_optimization
|
||||||
|
|
||||||
struct EncoderParameters {
|
struct EncoderParameters {
|
||||||
uint32_t target_bitrate;
|
BitrateAllocation target_bitrate;
|
||||||
uint8_t loss_rate;
|
uint8_t loss_rate;
|
||||||
int64_t rtt;
|
int64_t rtt;
|
||||||
uint32_t input_frame_rate;
|
uint32_t input_frame_rate;
|
||||||
|
|||||||
@ -45,6 +45,9 @@ class MockVideoEncoder : public VideoEncoder {
|
|||||||
MOCK_METHOD0(Reset, int32_t());
|
MOCK_METHOD0(Reset, int32_t());
|
||||||
MOCK_METHOD2(SetChannelParameters, int32_t(uint32_t packetLoss, int64_t rtt));
|
MOCK_METHOD2(SetChannelParameters, int32_t(uint32_t packetLoss, int64_t rtt));
|
||||||
MOCK_METHOD2(SetRates, int32_t(uint32_t newBitRate, uint32_t frameRate));
|
MOCK_METHOD2(SetRates, int32_t(uint32_t newBitRate, uint32_t frameRate));
|
||||||
|
MOCK_METHOD2(SetRateAllocation,
|
||||||
|
int32_t(const BitrateAllocation& newBitRate,
|
||||||
|
uint32_t frameRate));
|
||||||
MOCK_METHOD1(SetPeriodicKeyFrames, int32_t(bool enable));
|
MOCK_METHOD1(SetPeriodicKeyFrames, int32_t(bool enable));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODEC_INITIALIZER_H_
|
||||||
|
#define WEBRTC_MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODEC_INITIALIZER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/video_send_stream.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class TemporalLayersFactory;
|
||||||
|
class VideoBitrateAllocator;
|
||||||
|
class VideoCodec;
|
||||||
|
class VideoEncoderConfig;
|
||||||
|
|
||||||
|
class VideoCodecInitializer {
|
||||||
|
public:
|
||||||
|
// Takes an EncoderSettings, a VideoEncoderConfig and the VideoStream
|
||||||
|
// configuration and translated them into the old school VideoCodec type.
|
||||||
|
// It also creates a VideoBitrateAllocator instance, suitable for the codec
|
||||||
|
// type used. For instance, VP8 will create an allocator than can handle
|
||||||
|
// simulcast and temporal layering.
|
||||||
|
// GetBitrateAllocator is called implicitly from here, no need to call again.
|
||||||
|
static bool SetupCodec(
|
||||||
|
const VideoEncoderConfig& config,
|
||||||
|
const VideoSendStream::Config::EncoderSettings settings,
|
||||||
|
const std::vector<VideoStream>& streams,
|
||||||
|
VideoCodec* codec,
|
||||||
|
std::unique_ptr<VideoBitrateAllocator>* bitrate_allocator);
|
||||||
|
|
||||||
|
// Create a bitrate allocator for the specified codec. |tl_factory| is
|
||||||
|
// optional, if it is populated, ownership of that instance will be
|
||||||
|
// transferred to the VideoBitrateAllocator instance.
|
||||||
|
static std::unique_ptr<VideoBitrateAllocator> CreateBitrateAllocator(
|
||||||
|
const VideoCodec& codec,
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static VideoCodec VideoEncoderConfigToVideoCodec(
|
||||||
|
const VideoEncoderConfig& config,
|
||||||
|
const std::vector<VideoStream>& streams,
|
||||||
|
const std::string& payload_name,
|
||||||
|
int payload_type);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODEC_INITIALIZER_H_
|
||||||
@ -35,6 +35,7 @@ class EncodedImageCallback;
|
|||||||
// removing the VCM and use VideoSender/VideoReceiver as a public interface
|
// removing the VCM and use VideoSender/VideoReceiver as a public interface
|
||||||
// directly.
|
// directly.
|
||||||
class VCMQMSettingsCallback;
|
class VCMQMSettingsCallback;
|
||||||
|
class VideoBitrateAllocator;
|
||||||
class VideoEncoder;
|
class VideoEncoder;
|
||||||
class VideoDecoder;
|
class VideoDecoder;
|
||||||
struct CodecSpecificInfo;
|
struct CodecSpecificInfo;
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/base/basictypes.h"
|
||||||
|
#include "webrtc/base/constructormagic.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
DefaultVideoBitrateAllocator::DefaultVideoBitrateAllocator(
|
||||||
|
const VideoCodec& codec)
|
||||||
|
: codec_(codec) {}
|
||||||
|
|
||||||
|
DefaultVideoBitrateAllocator::~DefaultVideoBitrateAllocator() {}
|
||||||
|
|
||||||
|
BitrateAllocation DefaultVideoBitrateAllocator::GetAllocation(
|
||||||
|
uint32_t total_bitrate_bps,
|
||||||
|
uint32_t framerate) {
|
||||||
|
BitrateAllocation allocation;
|
||||||
|
if (total_bitrate_bps == 0)
|
||||||
|
return allocation;
|
||||||
|
|
||||||
|
if (total_bitrate_bps < codec_.minBitrate * 1000) {
|
||||||
|
allocation.SetBitrate(0, 0, codec_.minBitrate * 1000);
|
||||||
|
} else if (codec_.maxBitrate > 0 &&
|
||||||
|
total_bitrate_bps > codec_.maxBitrate * 1000) {
|
||||||
|
allocation.SetBitrate(0, 0, codec_.maxBitrate * 1000);
|
||||||
|
} else {
|
||||||
|
allocation.SetBitrate(0, 0, total_bitrate_bps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DefaultVideoBitrateAllocator::GetPreferredBitrateBps(
|
||||||
|
uint32_t framerate) {
|
||||||
|
return GetAllocation(codec_.maxBitrate * 1000, framerate).get_sum_bps();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_DEFAULT_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
|
#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_DEFAULT_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
|
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class DefaultVideoBitrateAllocator : public VideoBitrateAllocator {
|
||||||
|
public:
|
||||||
|
explicit DefaultVideoBitrateAllocator(const VideoCodec& codec);
|
||||||
|
~DefaultVideoBitrateAllocator() override;
|
||||||
|
|
||||||
|
BitrateAllocation GetAllocation(uint32_t total_bitrate,
|
||||||
|
uint32_t framerate) override;
|
||||||
|
uint32_t GetPreferredBitrateBps(uint32_t framerate) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const VideoCodec codec_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_DEFAULT_VIDEO_BITRATE_ALLOCATOR_H_
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/test/gtest.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
uint32_t kMaxBitrateBps = 1000000;
|
||||||
|
uint32_t kMinBitrateBps = 50000;
|
||||||
|
uint32_t kMaxFramerate = 30;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class DefaultVideoBitrateAllocatorTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
DefaultVideoBitrateAllocatorTest() {}
|
||||||
|
virtual ~DefaultVideoBitrateAllocatorTest() {}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
codec_.codecType = kVideoCodecVP8;
|
||||||
|
codec_.minBitrate = kMinBitrateBps / 1000;
|
||||||
|
codec_.maxBitrate = kMaxBitrateBps / 1000;
|
||||||
|
codec_.targetBitrate = (kMinBitrateBps + kMaxBitrateBps) / 2000;
|
||||||
|
codec_.maxFramerate = kMaxFramerate;
|
||||||
|
allocator_.reset(new DefaultVideoBitrateAllocator(codec_));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
VideoCodec codec_;
|
||||||
|
std::unique_ptr<DefaultVideoBitrateAllocator> allocator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(DefaultVideoBitrateAllocatorTest, ZeroIsOff) {
|
||||||
|
BitrateAllocation allocation = allocator_->GetAllocation(0, kMaxFramerate);
|
||||||
|
EXPECT_EQ(0u, allocation.get_sum_bps());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DefaultVideoBitrateAllocatorTest, CapsToMin) {
|
||||||
|
BitrateAllocation allocation = allocator_->GetAllocation(1, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMinBitrateBps, allocation.get_sum_bps());
|
||||||
|
|
||||||
|
allocation = allocator_->GetAllocation(kMinBitrateBps - 1, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMinBitrateBps, allocation.get_sum_bps());
|
||||||
|
|
||||||
|
allocation = allocator_->GetAllocation(kMinBitrateBps, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMinBitrateBps, allocation.get_sum_bps());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DefaultVideoBitrateAllocatorTest, CapsToMax) {
|
||||||
|
BitrateAllocation allocation =
|
||||||
|
allocator_->GetAllocation(kMaxBitrateBps, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMaxBitrateBps, allocation.get_sum_bps());
|
||||||
|
|
||||||
|
allocation = allocator_->GetAllocation(kMaxBitrateBps + 1, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMaxBitrateBps, allocation.get_sum_bps());
|
||||||
|
|
||||||
|
allocation = allocator_->GetAllocation(std::numeric_limits<uint32_t>::max(),
|
||||||
|
kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMaxBitrateBps, allocation.get_sum_bps());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DefaultVideoBitrateAllocatorTest, GoodInBetween) {
|
||||||
|
BitrateAllocation allocation =
|
||||||
|
allocator_->GetAllocation(kMinBitrateBps + 1, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMinBitrateBps + 1, allocation.get_sum_bps());
|
||||||
|
|
||||||
|
allocation = allocator_->GetAllocation(kMaxBitrateBps - 1, kMaxFramerate);
|
||||||
|
EXPECT_EQ(kMaxBitrateBps - 1, allocation.get_sum_bps());
|
||||||
|
}
|
||||||
|
} // namespace webrtc
|
||||||
@ -11,67 +11,133 @@
|
|||||||
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
webrtc::SimulcastRateAllocator::SimulcastRateAllocator(const VideoCodec& codec)
|
SimulcastRateAllocator::SimulcastRateAllocator(
|
||||||
: codec_(codec) {}
|
const VideoCodec& codec,
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory)
|
||||||
|
: codec_(codec), tl_factory_(std::move(tl_factory)) {
|
||||||
|
if (tl_factory_.get())
|
||||||
|
tl_factory_->SetListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> webrtc::SimulcastRateAllocator::GetAllocation(
|
void SimulcastRateAllocator::OnTemporalLayersCreated(int simulcast_id,
|
||||||
uint32_t bitrate_kbps) const {
|
TemporalLayers* layers) {
|
||||||
// Always allocate enough bitrate for the minimum bitrate of the first layer.
|
RTC_DCHECK(temporal_layers_.find(simulcast_id) == temporal_layers_.end());
|
||||||
// Suspending below min bitrate is controlled outside the codec implementation
|
temporal_layers_[simulcast_id] = layers;
|
||||||
// and is not overridden by this.
|
}
|
||||||
const uint32_t min_bitrate_bps = codec_.numberOfSimulcastStreams == 0
|
|
||||||
? codec_.minBitrate
|
|
||||||
: codec_.simulcastStream[0].minBitrate;
|
|
||||||
uint32_t left_to_allocate = std::max(min_bitrate_bps, bitrate_kbps);
|
|
||||||
if (codec_.maxBitrate)
|
|
||||||
left_to_allocate = std::min(left_to_allocate, codec_.maxBitrate);
|
|
||||||
|
|
||||||
if (codec_.numberOfSimulcastStreams < 2) {
|
BitrateAllocation SimulcastRateAllocator::GetAllocation(
|
||||||
|
uint32_t total_bitrate_bps,
|
||||||
|
uint32_t framerate) {
|
||||||
|
uint32_t left_to_allocate = total_bitrate_bps;
|
||||||
|
if (codec_.maxBitrate && codec_.maxBitrate * 1000 < left_to_allocate)
|
||||||
|
left_to_allocate = codec_.maxBitrate * 1000;
|
||||||
|
|
||||||
|
BitrateAllocation allocated_bitrates_bps;
|
||||||
|
if (codec_.numberOfSimulcastStreams == 0) {
|
||||||
// No simulcast, just set the target as this has been capped already.
|
// No simulcast, just set the target as this has been capped already.
|
||||||
return std::vector<uint32_t>(1, left_to_allocate);
|
allocated_bitrates_bps.SetBitrate(
|
||||||
|
0, 0, std::max(codec_.minBitrate * 1000, left_to_allocate));
|
||||||
|
} else {
|
||||||
|
// Always allocate enough bitrate for the minimum bitrate of the first
|
||||||
|
// layer. Suspending below min bitrate is controlled outside the codec
|
||||||
|
// implementation and is not overridden by this.
|
||||||
|
left_to_allocate =
|
||||||
|
std::max(codec_.simulcastStream[0].minBitrate * 1000, left_to_allocate);
|
||||||
|
|
||||||
|
// Begin by allocating bitrate to simulcast streams, putting all bitrate in
|
||||||
|
// temporal layer 0. We'll then distribute this bitrate, across potential
|
||||||
|
// temporal layers, when stream allocation is done.
|
||||||
|
|
||||||
|
// Allocate up to the target bitrate for each simulcast layer.
|
||||||
|
size_t layer = 0;
|
||||||
|
for (; layer < codec_.numberOfSimulcastStreams; ++layer) {
|
||||||
|
const SimulcastStream& stream = codec_.simulcastStream[layer];
|
||||||
|
if (left_to_allocate < stream.minBitrate * 1000)
|
||||||
|
break;
|
||||||
|
uint32_t allocation =
|
||||||
|
std::min(left_to_allocate, stream.targetBitrate * 1000);
|
||||||
|
allocated_bitrates_bps.SetBitrate(layer, 0, allocation);
|
||||||
|
RTC_DCHECK_LE(allocation, left_to_allocate);
|
||||||
|
left_to_allocate -= allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, try allocate remaining bitrate, up to max bitrate, in top stream.
|
||||||
|
// TODO(sprang): Allocate up to max bitrate for all layers once we have a
|
||||||
|
// better idea of possible performance implications.
|
||||||
|
if (left_to_allocate > 0) {
|
||||||
|
size_t active_layer = layer - 1;
|
||||||
|
const SimulcastStream& stream = codec_.simulcastStream[active_layer];
|
||||||
|
uint32_t bitrate_bps =
|
||||||
|
allocated_bitrates_bps.GetSpatialLayerSum(active_layer);
|
||||||
|
uint32_t allocation =
|
||||||
|
std::min(left_to_allocate, stream.maxBitrate * 1000 - bitrate_bps);
|
||||||
|
bitrate_bps += allocation;
|
||||||
|
RTC_DCHECK_LE(allocation, left_to_allocate);
|
||||||
|
left_to_allocate -= allocation;
|
||||||
|
allocated_bitrates_bps.SetBitrate(active_layer, 0, bitrate_bps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize bitrates with zeroes.
|
const int num_spatial_streams =
|
||||||
std::vector<uint32_t> allocated_bitrates_bps(codec_.numberOfSimulcastStreams,
|
std::max(1, static_cast<int>(codec_.numberOfSimulcastStreams));
|
||||||
0);
|
|
||||||
|
|
||||||
// First try to allocate up to the target bitrate for each substream.
|
// Finally, distribute the bitrate for the simulcast streams across the
|
||||||
size_t layer = 0;
|
// available temporal layers.
|
||||||
for (; layer < codec_.numberOfSimulcastStreams; ++layer) {
|
for (int simulcast_id = 0; simulcast_id < num_spatial_streams;
|
||||||
const SimulcastStream& stream = codec_.simulcastStream[layer];
|
++simulcast_id) {
|
||||||
if (left_to_allocate < stream.minBitrate)
|
auto tl_it = temporal_layers_.find(simulcast_id);
|
||||||
break;
|
if (tl_it == temporal_layers_.end())
|
||||||
uint32_t allocation = std::min(left_to_allocate, stream.targetBitrate);
|
continue; // TODO(sprang): If > 1 SS, assume default TL alloc?
|
||||||
allocated_bitrates_bps[layer] = allocation;
|
|
||||||
left_to_allocate -= allocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, try allocate remaining bitrate, up to max bitrate, in top layer.
|
uint32_t target_bitrate_kbps =
|
||||||
// TODO(sprang): Allocate up to max bitrate for all layers once we have a
|
allocated_bitrates_bps.GetBitrate(simulcast_id, 0) / 1000;
|
||||||
// better idea of possible performance implications.
|
RTC_DCHECK_EQ(
|
||||||
if (left_to_allocate > 0) {
|
target_bitrate_kbps,
|
||||||
size_t active_layer = layer - 1;
|
allocated_bitrates_bps.GetSpatialLayerSum(simulcast_id) / 1000);
|
||||||
const SimulcastStream& stream = codec_.simulcastStream[active_layer];
|
uint32_t max_bitrate_kbps;
|
||||||
uint32_t allocation =
|
if (codec_.numberOfSimulcastStreams == 0) {
|
||||||
std::min(left_to_allocate,
|
max_bitrate_kbps = codec_.maxBitrate;
|
||||||
stream.maxBitrate - allocated_bitrates_bps[active_layer]);
|
|
||||||
left_to_allocate -= allocation;
|
// TODO(holmer): This is a temporary hack for screensharing, where we
|
||||||
allocated_bitrates_bps[active_layer] += allocation;
|
// interpret the startBitrate as the encoder target bitrate. This is
|
||||||
|
// to allow for a different max bitrate, so if the codec can't meet
|
||||||
|
// the target we still allow it to overshoot up to the max before dropping
|
||||||
|
// frames. This hack should be improved.
|
||||||
|
if (codec_.mode == kScreensharing && codec_.targetBitrate > 0 &&
|
||||||
|
(codec_.codecSpecific.VP8.numberOfTemporalLayers == 2 ||
|
||||||
|
codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
|
||||||
|
int tl0_bitrate = std::min(codec_.targetBitrate, target_bitrate_kbps);
|
||||||
|
max_bitrate_kbps = std::min(codec_.maxBitrate, target_bitrate_kbps);
|
||||||
|
target_bitrate_kbps = tl0_bitrate;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
max_bitrate_kbps = codec_.simulcastStream[simulcast_id].maxBitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> tl_allocation = tl_it->second->OnRatesUpdated(
|
||||||
|
target_bitrate_kbps, max_bitrate_kbps, framerate);
|
||||||
|
|
||||||
|
for (size_t tl_index = 0; tl_index < tl_allocation.size(); ++tl_index) {
|
||||||
|
allocated_bitrates_bps.SetBitrate(simulcast_id, tl_index,
|
||||||
|
tl_allocation[tl_index] * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocated_bitrates_bps;
|
return allocated_bitrates_bps;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SimulcastRateAllocator::GetPreferedBitrate() const {
|
uint32_t SimulcastRateAllocator::GetPreferredBitrateBps(uint32_t framerate) {
|
||||||
std::vector<uint32_t> rates = GetAllocation(codec_.maxBitrate);
|
BitrateAllocation allocation =
|
||||||
uint32_t preferred_bitrate = 0;
|
GetAllocation(codec_.maxBitrate * 1000, framerate);
|
||||||
for (const uint32_t& rate : rates) {
|
return allocation.get_sum_bps();
|
||||||
preferred_bitrate += rate;
|
|
||||||
}
|
|
||||||
return preferred_bitrate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoCodec& webrtc::SimulcastRateAllocator::GetCodec() const {
|
const VideoCodec& webrtc::SimulcastRateAllocator::GetCodec() const {
|
||||||
|
|||||||
@ -11,24 +11,36 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
|
||||||
|
|
||||||
#include <vector>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "webrtc/base/basictypes.h"
|
#include "webrtc/base/basictypes.h"
|
||||||
#include "webrtc/base/constructormagic.h"
|
#include "webrtc/base/constructormagic.h"
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/video_encoder.h"
|
#include "webrtc/video_encoder.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class SimulcastRateAllocator {
|
class SimulcastRateAllocator : public VideoBitrateAllocator,
|
||||||
|
public TemporalLayersListener {
|
||||||
public:
|
public:
|
||||||
explicit SimulcastRateAllocator(const VideoCodec& codec);
|
explicit SimulcastRateAllocator(
|
||||||
|
const VideoCodec& codec,
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory);
|
||||||
|
|
||||||
std::vector<uint32_t> GetAllocation(uint32_t bitrate_kbps) const;
|
void OnTemporalLayersCreated(int simulcast_id,
|
||||||
uint32_t GetPreferedBitrate() const;
|
TemporalLayers* layers) override;
|
||||||
|
|
||||||
|
BitrateAllocation GetAllocation(uint32_t total_bitrate_bps,
|
||||||
|
uint32_t framerate) override;
|
||||||
|
uint32_t GetPreferredBitrateBps(uint32_t framerate) override;
|
||||||
const VideoCodec& GetCodec() const;
|
const VideoCodec& GetCodec() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const VideoCodec codec_;
|
const VideoCodec codec_;
|
||||||
|
std::map<uint32_t, TemporalLayers*> temporal_layers_;
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory_;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator);
|
RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
|
|
||||||
@ -41,43 +42,59 @@ class SimulcastRateAllocatorTest : public ::testing::Test {
|
|||||||
EXPECT_EQ(expected[i], actual[i]) << "Mismatch at index " << i;
|
EXPECT_EQ(expected[i], actual[i]) << "Mismatch at index " << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <size_t S>
|
||||||
|
void ExpectEqual(uint32_t (&expected)[S], const BitrateAllocation& actual) {
|
||||||
|
// EXPECT_EQ(S, actual.size());
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for (size_t i = 0; i < S; ++i) {
|
||||||
|
uint32_t layer_bitrate = actual.GetSpatialLayerSum(i);
|
||||||
|
EXPECT_EQ(expected[i] * 1000, layer_bitrate) << "Mismatch at index " << i;
|
||||||
|
sum += layer_bitrate;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(sum, actual.get_sum_bps());
|
||||||
|
}
|
||||||
|
|
||||||
void CreateAllocator() {
|
void CreateAllocator() {
|
||||||
allocator_.reset(new SimulcastRateAllocator(codec_));
|
allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
BitrateAllocation GetAllocation(uint32_t target_bitrate) {
|
||||||
|
return allocator_->GetAllocation(target_bitrate * 1000, kDefaultFrameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
static const int kDefaultFrameRate = 30;
|
||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
std::unique_ptr<SimulcastRateAllocator> allocator_;
|
std::unique_ptr<SimulcastRateAllocator> allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, NoSimulcastBelowMin) {
|
TEST_F(SimulcastRateAllocatorTest, NoSimulcastBelowMin) {
|
||||||
uint32_t expected[] = {codec_.minBitrate};
|
uint32_t expected[] = {codec_.minBitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(codec_.minBitrate - 1));
|
ExpectEqual(expected, GetAllocation(codec_.minBitrate - 1));
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(1));
|
ExpectEqual(expected, GetAllocation(1));
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(0));
|
ExpectEqual(expected, GetAllocation(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, NoSimulcastAboveMax) {
|
TEST_F(SimulcastRateAllocatorTest, NoSimulcastAboveMax) {
|
||||||
uint32_t expected[] = {codec_.maxBitrate};
|
uint32_t expected[] = {codec_.maxBitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(codec_.maxBitrate + 1));
|
ExpectEqual(expected, GetAllocation(codec_.maxBitrate + 1));
|
||||||
ExpectEqual(expected,
|
ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
|
||||||
allocator_->GetAllocation(std::numeric_limits<uint32_t>::max()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, NoSimulcastNoMax) {
|
TEST_F(SimulcastRateAllocatorTest, NoSimulcastNoMax) {
|
||||||
constexpr uint32_t kMax = std::numeric_limits<uint32_t>::max();
|
const uint32_t kMax = BitrateAllocation::kMaxBitrateBps / 1000;
|
||||||
codec_.maxBitrate = 0;
|
codec_.maxBitrate = 0;
|
||||||
CreateAllocator();
|
CreateAllocator();
|
||||||
|
|
||||||
uint32_t expected[] = {kMax};
|
uint32_t expected[] = {kMax};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(kMax));
|
ExpectEqual(expected, GetAllocation(kMax));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, NoSimulcastWithinLimits) {
|
TEST_F(SimulcastRateAllocatorTest, NoSimulcastWithinLimits) {
|
||||||
for (uint32_t bitrate = codec_.minBitrate; bitrate <= codec_.maxBitrate;
|
for (uint32_t bitrate = codec_.minBitrate; bitrate <= codec_.maxBitrate;
|
||||||
++bitrate) {
|
++bitrate) {
|
||||||
uint32_t expected[] = {bitrate};
|
uint32_t expected[] = {bitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +107,9 @@ TEST_F(SimulcastRateAllocatorTest, SingleSimulcastBelowMin) {
|
|||||||
CreateAllocator();
|
CreateAllocator();
|
||||||
|
|
||||||
uint32_t expected[] = {kMin};
|
uint32_t expected[] = {kMin};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(kMin - 1));
|
ExpectEqual(expected, GetAllocation(kMin - 1));
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(1));
|
ExpectEqual(expected, GetAllocation(1));
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(0));
|
ExpectEqual(expected, GetAllocation(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
|
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
|
||||||
@ -103,9 +120,9 @@ TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
|
|||||||
CreateAllocator();
|
CreateAllocator();
|
||||||
|
|
||||||
uint32_t expected[] = {kMax};
|
uint32_t expected[] = {kMax};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(kMax + 1));
|
ExpectEqual(expected, GetAllocation(kMax));
|
||||||
ExpectEqual(expected,
|
ExpectEqual(expected, GetAllocation(kMax + 1));
|
||||||
allocator_->GetAllocation(std::numeric_limits<uint32_t>::max()));
|
ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
|
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
|
||||||
@ -117,7 +134,7 @@ TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
|
|||||||
|
|
||||||
for (uint32_t bitrate = kMinBitrate; bitrate <= kMaxBitrate; ++bitrate) {
|
for (uint32_t bitrate = kMinBitrate; bitrate <= kMaxBitrate; ++bitrate) {
|
||||||
uint32_t expected[] = {bitrate};
|
uint32_t expected[] = {bitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,14 +156,14 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
// Single stream, min bitrate.
|
// Single stream, min bitrate.
|
||||||
const uint32_t bitrate = codec_.simulcastStream[0].minBitrate;
|
const uint32_t bitrate = codec_.simulcastStream[0].minBitrate;
|
||||||
uint32_t expected[] = {bitrate, 0, 0};
|
uint32_t expected[] = {bitrate, 0, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Single stream at target bitrate.
|
// Single stream at target bitrate.
|
||||||
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate;
|
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate;
|
||||||
uint32_t expected[] = {bitrate, 0, 0};
|
uint32_t expected[] = {bitrate, 0, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -154,7 +171,7 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
|
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
|
||||||
codec_.simulcastStream[1].minBitrate - 1;
|
codec_.simulcastStream[1].minBitrate - 1;
|
||||||
uint32_t expected[] = {bitrate, 0, 0};
|
uint32_t expected[] = {bitrate, 0, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -163,7 +180,7 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
codec_.simulcastStream[1].minBitrate;
|
codec_.simulcastStream[1].minBitrate;
|
||||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||||
codec_.simulcastStream[1].minBitrate, 0};
|
codec_.simulcastStream[1].minBitrate, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -172,7 +189,7 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
codec_.simulcastStream[1].maxBitrate;
|
codec_.simulcastStream[1].maxBitrate;
|
||||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||||
codec_.simulcastStream[1].maxBitrate, 0};
|
codec_.simulcastStream[1].maxBitrate, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -182,7 +199,7 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
codec_.simulcastStream[1].maxBitrate + 499;
|
codec_.simulcastStream[1].maxBitrate + 499;
|
||||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||||
codec_.simulcastStream[1].maxBitrate, 0};
|
codec_.simulcastStream[1].maxBitrate, 0};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -193,7 +210,7 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||||
codec_.simulcastStream[1].targetBitrate,
|
codec_.simulcastStream[1].targetBitrate,
|
||||||
codec_.simulcastStream[2].minBitrate};
|
codec_.simulcastStream[2].minBitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -204,12 +221,13 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
|||||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||||
codec_.simulcastStream[1].targetBitrate,
|
codec_.simulcastStream[1].targetBitrate,
|
||||||
codec_.simulcastStream[2].maxBitrate};
|
codec_.simulcastStream[2].maxBitrate};
|
||||||
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
|
ExpectEqual(expected, GetAllocation(bitrate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrate) {
|
TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrateBps) {
|
||||||
EXPECT_EQ(codec_.maxBitrate, allocator_->GetPreferedBitrate());
|
EXPECT_EQ(codec_.maxBitrate * 1000,
|
||||||
|
allocator_->GetPreferredBitrateBps(codec_.maxFramerate));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrateSimulcast) {
|
TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrateSimulcast) {
|
||||||
@ -228,12 +246,13 @@ TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrateSimulcast) {
|
|||||||
codec_.simulcastStream[2].maxBitrate = 4000;
|
codec_.simulcastStream[2].maxBitrate = 4000;
|
||||||
CreateAllocator();
|
CreateAllocator();
|
||||||
|
|
||||||
uint32_t preferred_bitrate;
|
uint32_t preferred_bitrate_kbps;
|
||||||
preferred_bitrate = codec_.simulcastStream[0].targetBitrate;
|
preferred_bitrate_kbps = codec_.simulcastStream[0].targetBitrate;
|
||||||
preferred_bitrate += codec_.simulcastStream[1].targetBitrate;
|
preferred_bitrate_kbps += codec_.simulcastStream[1].targetBitrate;
|
||||||
preferred_bitrate += codec_.simulcastStream[2].maxBitrate;
|
preferred_bitrate_kbps += codec_.simulcastStream[2].maxBitrate;
|
||||||
|
|
||||||
EXPECT_EQ(preferred_bitrate, allocator_->GetPreferedBitrate());
|
EXPECT_EQ(preferred_bitrate_kbps * 1000,
|
||||||
|
allocator_->GetPreferredBitrateBps(codec_.maxFramerate));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
|
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'default_video_bitrate_allocator.cc',
|
||||||
|
'default_video_bitrate_allocator.h',
|
||||||
'frame_dropper.cc',
|
'frame_dropper.cc',
|
||||||
'frame_dropper.h',
|
'frame_dropper.h',
|
||||||
'ivf_file_writer.cc',
|
'ivf_file_writer.cc',
|
||||||
|
|||||||
229
webrtc/modules/video_coding/video_codec_initializer.cc
Normal file
229
webrtc/modules/video_coding/video_codec_initializer.cc
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
|
||||||
|
|
||||||
|
#include "webrtc/base/basictypes.h"
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/common_types.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/system_wrappers/include/clock.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory {
|
||||||
|
ScreenshareTemporalLayersFactory() {}
|
||||||
|
virtual ~ScreenshareTemporalLayersFactory() {}
|
||||||
|
|
||||||
|
virtual webrtc::TemporalLayers* Create(int simulcast_id,
|
||||||
|
int num_temporal_layers,
|
||||||
|
uint8_t initial_tl0_pic_idx) const {
|
||||||
|
webrtc::TemporalLayers* tl = new webrtc::ScreenshareLayers(
|
||||||
|
num_temporal_layers, rand(), webrtc::Clock::GetRealTimeClock());
|
||||||
|
if (listener_)
|
||||||
|
listener_->OnTemporalLayersCreated(simulcast_id, tl);
|
||||||
|
return tl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VideoCodecInitializer::SetupCodec(
|
||||||
|
const VideoEncoderConfig& config,
|
||||||
|
const VideoSendStream::Config::EncoderSettings settings,
|
||||||
|
const std::vector<VideoStream>& streams,
|
||||||
|
VideoCodec* codec,
|
||||||
|
std::unique_ptr<VideoBitrateAllocator>* bitrate_allocator) {
|
||||||
|
*codec = VideoEncoderConfigToVideoCodec(
|
||||||
|
config, streams, settings.payload_name, settings.payload_type);
|
||||||
|
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory;
|
||||||
|
switch (codec->codecType) {
|
||||||
|
case kVideoCodecVP8: {
|
||||||
|
if (!codec->VP8()->tl_factory) {
|
||||||
|
if (codec->mode == kScreensharing &&
|
||||||
|
codec->numberOfSimulcastStreams == 1 &&
|
||||||
|
codec->VP8()->numberOfTemporalLayers == 2) {
|
||||||
|
// Conference mode temporal layering for screen content.
|
||||||
|
tl_factory.reset(new ScreenshareTemporalLayersFactory());
|
||||||
|
} else {
|
||||||
|
// Standard video temporal layers.
|
||||||
|
tl_factory.reset(new TemporalLayersFactory());
|
||||||
|
}
|
||||||
|
codec->VP8()->tl_factory = tl_factory.get();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// TODO(sprang): Warn, once we have specific allocators for all supported
|
||||||
|
// codec types.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*bitrate_allocator = CreateBitrateAllocator(*codec, std::move(tl_factory));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<VideoBitrateAllocator>
|
||||||
|
VideoCodecInitializer::CreateBitrateAllocator(
|
||||||
|
const VideoCodec& codec,
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory) {
|
||||||
|
std::unique_ptr<VideoBitrateAllocator> rate_allocator;
|
||||||
|
|
||||||
|
switch (codec.codecType) {
|
||||||
|
case kVideoCodecVP8: {
|
||||||
|
// Set up default VP8 temporal layer factory, if not provided.
|
||||||
|
rate_allocator.reset(
|
||||||
|
new SimulcastRateAllocator(codec, std::move(tl_factory)));
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
rate_allocator.reset(new DefaultVideoBitrateAllocator(codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rate_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(sprang): Split this up and separate the codec specific parts.
|
||||||
|
VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec(
|
||||||
|
const VideoEncoderConfig& config,
|
||||||
|
const std::vector<VideoStream>& streams,
|
||||||
|
const std::string& payload_name,
|
||||||
|
int payload_type) {
|
||||||
|
static const int kEncoderMinBitrateKbps = 30;
|
||||||
|
RTC_DCHECK(!streams.empty());
|
||||||
|
RTC_DCHECK_GE(config.min_transmit_bitrate_bps, 0);
|
||||||
|
|
||||||
|
VideoCodec video_codec;
|
||||||
|
memset(&video_codec, 0, sizeof(video_codec));
|
||||||
|
video_codec.codecType = PayloadNameToCodecType(payload_name)
|
||||||
|
.value_or(VideoCodecType::kVideoCodecGeneric);
|
||||||
|
|
||||||
|
switch (config.content_type) {
|
||||||
|
case VideoEncoderConfig::ContentType::kRealtimeVideo:
|
||||||
|
video_codec.mode = kRealtimeVideo;
|
||||||
|
break;
|
||||||
|
case VideoEncoderConfig::ContentType::kScreen:
|
||||||
|
video_codec.mode = kScreensharing;
|
||||||
|
if (streams.size() == 1 &&
|
||||||
|
streams[0].temporal_layer_thresholds_bps.size() == 1) {
|
||||||
|
video_codec.targetBitrate =
|
||||||
|
streams[0].temporal_layer_thresholds_bps[0] / 1000;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.encoder_specific_settings)
|
||||||
|
config.encoder_specific_settings->FillEncoderSpecificSettings(&video_codec);
|
||||||
|
|
||||||
|
switch (video_codec.codecType) {
|
||||||
|
case kVideoCodecVP8: {
|
||||||
|
if (!config.encoder_specific_settings)
|
||||||
|
video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
|
||||||
|
video_codec.codecSpecific.VP8.numberOfTemporalLayers =
|
||||||
|
static_cast<unsigned char>(
|
||||||
|
streams.back().temporal_layer_thresholds_bps.size() + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kVideoCodecVP9: {
|
||||||
|
if (!config.encoder_specific_settings)
|
||||||
|
video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
|
||||||
|
if (video_codec.mode == kScreensharing &&
|
||||||
|
config.encoder_specific_settings) {
|
||||||
|
video_codec.codecSpecific.VP9.flexibleMode = true;
|
||||||
|
// For now VP9 screensharing use 1 temporal and 2 spatial layers.
|
||||||
|
RTC_DCHECK_EQ(1, video_codec.codecSpecific.VP9.numberOfTemporalLayers);
|
||||||
|
RTC_DCHECK_EQ(2, video_codec.codecSpecific.VP9.numberOfSpatialLayers);
|
||||||
|
}
|
||||||
|
video_codec.codecSpecific.VP9.numberOfTemporalLayers =
|
||||||
|
static_cast<unsigned char>(
|
||||||
|
streams.back().temporal_layer_thresholds_bps.size() + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kVideoCodecH264: {
|
||||||
|
if (!config.encoder_specific_settings)
|
||||||
|
video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// TODO(pbos): Support encoder_settings codec-agnostically.
|
||||||
|
RTC_DCHECK(!config.encoder_specific_settings)
|
||||||
|
<< "Encoder-specific settings for codec type not wired up.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(video_codec.plName, payload_name.c_str(), kPayloadNameSize - 1);
|
||||||
|
video_codec.plName[kPayloadNameSize - 1] = '\0';
|
||||||
|
video_codec.plType = payload_type;
|
||||||
|
video_codec.numberOfSimulcastStreams =
|
||||||
|
static_cast<unsigned char>(streams.size());
|
||||||
|
video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
|
||||||
|
if (video_codec.minBitrate < kEncoderMinBitrateKbps)
|
||||||
|
video_codec.minBitrate = kEncoderMinBitrateKbps;
|
||||||
|
RTC_DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
|
||||||
|
if (video_codec.codecType == kVideoCodecVP9) {
|
||||||
|
// If the vector is empty, bitrates will be configured automatically.
|
||||||
|
RTC_DCHECK(config.spatial_layers.empty() ||
|
||||||
|
config.spatial_layers.size() ==
|
||||||
|
video_codec.codecSpecific.VP9.numberOfSpatialLayers);
|
||||||
|
RTC_DCHECK_LE(video_codec.codecSpecific.VP9.numberOfSpatialLayers,
|
||||||
|
kMaxSimulcastStreams);
|
||||||
|
for (size_t i = 0; i < config.spatial_layers.size(); ++i)
|
||||||
|
video_codec.spatialLayers[i] = config.spatial_layers[i];
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < streams.size(); ++i) {
|
||||||
|
SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
|
||||||
|
RTC_DCHECK_GT(streams[i].width, 0u);
|
||||||
|
RTC_DCHECK_GT(streams[i].height, 0u);
|
||||||
|
RTC_DCHECK_GT(streams[i].max_framerate, 0);
|
||||||
|
// Different framerates not supported per stream at the moment.
|
||||||
|
RTC_DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
|
||||||
|
RTC_DCHECK_GE(streams[i].min_bitrate_bps, 0);
|
||||||
|
RTC_DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
|
||||||
|
RTC_DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
|
||||||
|
RTC_DCHECK_GE(streams[i].max_qp, 0);
|
||||||
|
|
||||||
|
sim_stream->width = static_cast<uint16_t>(streams[i].width);
|
||||||
|
sim_stream->height = static_cast<uint16_t>(streams[i].height);
|
||||||
|
sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
|
||||||
|
sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
|
||||||
|
sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
|
||||||
|
sim_stream->qpMax = streams[i].max_qp;
|
||||||
|
sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
|
||||||
|
streams[i].temporal_layer_thresholds_bps.size() + 1);
|
||||||
|
|
||||||
|
video_codec.width =
|
||||||
|
std::max(video_codec.width, static_cast<uint16_t>(streams[i].width));
|
||||||
|
video_codec.height =
|
||||||
|
std::max(video_codec.height, static_cast<uint16_t>(streams[i].height));
|
||||||
|
video_codec.minBitrate =
|
||||||
|
std::min(static_cast<uint16_t>(video_codec.minBitrate),
|
||||||
|
static_cast<uint16_t>(streams[i].min_bitrate_bps / 1000));
|
||||||
|
video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
|
||||||
|
video_codec.qpMax = std::max(video_codec.qpMax,
|
||||||
|
static_cast<unsigned int>(streams[i].max_qp));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (video_codec.maxBitrate == 0) {
|
||||||
|
// Unset max bitrate -> cap to one bit per pixel.
|
||||||
|
video_codec.maxBitrate =
|
||||||
|
(video_codec.width * video_codec.height * video_codec.maxFramerate) /
|
||||||
|
1000;
|
||||||
|
}
|
||||||
|
if (video_codec.maxBitrate < kEncoderMinBitrateKbps)
|
||||||
|
video_codec.maxBitrate = kEncoderMinBitrateKbps;
|
||||||
|
|
||||||
|
RTC_DCHECK_GT(streams[0].max_framerate, 0);
|
||||||
|
video_codec.maxFramerate = streams[0].max_framerate;
|
||||||
|
return video_codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -23,6 +23,7 @@
|
|||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
# interfaces
|
# interfaces
|
||||||
|
'include/video_codec_initializer.h',
|
||||||
'include/video_coding.h',
|
'include/video_coding.h',
|
||||||
'include/video_coding_defines.h',
|
'include/video_coding_defines.h',
|
||||||
|
|
||||||
@ -86,6 +87,7 @@
|
|||||||
'session_info.cc',
|
'session_info.cc',
|
||||||
'timestamp_map.cc',
|
'timestamp_map.cc',
|
||||||
'timing.cc',
|
'timing.cc',
|
||||||
|
'video_codec_initializer.cc',
|
||||||
'video_coding_impl.cc',
|
'video_coding_impl.cc',
|
||||||
'video_sender.cc',
|
'video_sender.cc',
|
||||||
'video_receiver.cc',
|
'video_receiver.cc',
|
||||||
|
|||||||
@ -11,10 +11,14 @@
|
|||||||
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||||
#include "webrtc/base/criticalsection.h"
|
#include "webrtc/base/criticalsection.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
|
#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/encoded_frame.h"
|
#include "webrtc/modules/video_coding/encoded_frame.h"
|
||||||
#include "webrtc/modules/video_coding/jitter_buffer.h"
|
#include "webrtc/modules/video_coding/jitter_buffer.h"
|
||||||
@ -104,6 +108,21 @@ class VideoCodingModuleImpl : public VideoCodingModule {
|
|||||||
int32_t RegisterSendCodec(const VideoCodec* sendCodec,
|
int32_t RegisterSendCodec(const VideoCodec* sendCodec,
|
||||||
uint32_t numberOfCores,
|
uint32_t numberOfCores,
|
||||||
uint32_t maxPayloadSize) override {
|
uint32_t maxPayloadSize) override {
|
||||||
|
if (sendCodec != nullptr && sendCodec->codecType == kVideoCodecVP8) {
|
||||||
|
// Set up a rate allocator and temporal layers factory for this vp8
|
||||||
|
// instance. The codec impl will have a raw pointer to the TL factory,
|
||||||
|
// and will call it when initializing. Since this can happen
|
||||||
|
// asynchronously keep the instance alive until destruction or until a
|
||||||
|
// new send codec is registered.
|
||||||
|
VideoCodec vp8_codec = *sendCodec;
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory(
|
||||||
|
new TemporalLayersFactory());
|
||||||
|
vp8_codec.VP8()->tl_factory = tl_factory.get();
|
||||||
|
rate_allocator_ = VideoCodecInitializer::CreateBitrateAllocator(
|
||||||
|
vp8_codec, std::move(tl_factory));
|
||||||
|
return sender_.RegisterSendCodec(&vp8_codec, numberOfCores,
|
||||||
|
maxPayloadSize);
|
||||||
|
}
|
||||||
return sender_.RegisterSendCodec(sendCodec, numberOfCores, maxPayloadSize);
|
return sender_.RegisterSendCodec(sendCodec, numberOfCores, maxPayloadSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +145,8 @@ class VideoCodingModuleImpl : public VideoCodingModule {
|
|||||||
int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s.
|
int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s.
|
||||||
uint8_t lossRate,
|
uint8_t lossRate,
|
||||||
int64_t rtt) override {
|
int64_t rtt) override {
|
||||||
return sender_.SetChannelParameters(target_bitrate, lossRate, rtt);
|
return sender_.SetChannelParameters(target_bitrate, lossRate, rtt,
|
||||||
|
rate_allocator_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t RegisterProtectionCallback(
|
int32_t RegisterProtectionCallback(
|
||||||
@ -256,6 +276,7 @@ class VideoCodingModuleImpl : public VideoCodingModule {
|
|||||||
private:
|
private:
|
||||||
EncodedImageCallbackWrapper post_encode_callback_;
|
EncodedImageCallbackWrapper post_encode_callback_;
|
||||||
vcm::VideoSender sender_;
|
vcm::VideoSender sender_;
|
||||||
|
std::unique_ptr<VideoBitrateAllocator> rate_allocator_;
|
||||||
vcm::VideoReceiver receiver_;
|
vcm::VideoReceiver receiver_;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
class VideoBitrateAllocator;
|
||||||
|
|
||||||
namespace vcm {
|
namespace vcm {
|
||||||
|
|
||||||
class VCMProcessTimer {
|
class VCMProcessTimer {
|
||||||
@ -75,9 +77,15 @@ class VideoSender : public Module {
|
|||||||
int Bitrate(unsigned int* bitrate) const;
|
int Bitrate(unsigned int* bitrate) const;
|
||||||
int FrameRate(unsigned int* framerate) const;
|
int FrameRate(unsigned int* framerate) const;
|
||||||
|
|
||||||
int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s.
|
int32_t SetChannelParameters(uint32_t target_bitrate_bps,
|
||||||
uint8_t lossRate,
|
uint8_t lossRate,
|
||||||
int64_t rtt);
|
int64_t rtt,
|
||||||
|
VideoBitrateAllocator* bitrate_allocator);
|
||||||
|
// Calls SetChannelParameters(), with the previous target bitrate, loss rate
|
||||||
|
// and RTT, but reallocates the bitrate allocation based on a presumably
|
||||||
|
// updated codec configuration.
|
||||||
|
int32_t UpdateChannelParemeters(VideoBitrateAllocator* bitrate_allocator);
|
||||||
|
|
||||||
// Deprecated:
|
// Deprecated:
|
||||||
// TODO(perkj): Remove once no projects use it.
|
// TODO(perkj): Remove once no projects use it.
|
||||||
int32_t RegisterProtectionCallback(VCMProtectionCallback* protection);
|
int32_t RegisterProtectionCallback(VCMProtectionCallback* protection);
|
||||||
|
|||||||
@ -14,9 +14,12 @@
|
|||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/base/logging.h"
|
#include "webrtc/base/logging.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/encoded_frame.h"
|
#include "webrtc/modules/video_coding/encoded_frame.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
#include "webrtc/modules/video_coding/utility/quality_scaler.h"
|
#include "webrtc/modules/video_coding/utility/quality_scaler.h"
|
||||||
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
||||||
#include "webrtc/system_wrappers/include/clock.h"
|
#include "webrtc/system_wrappers/include/clock.h"
|
||||||
@ -36,7 +39,7 @@ VideoSender::VideoSender(Clock* clock,
|
|||||||
frame_dropper_enabled_(true),
|
frame_dropper_enabled_(true),
|
||||||
_sendStatsTimer(1000, clock_),
|
_sendStatsTimer(1000, clock_),
|
||||||
current_codec_(),
|
current_codec_(),
|
||||||
encoder_params_({0, 0, 0, 0}),
|
encoder_params_({BitrateAllocation(), 0, 0, 0}),
|
||||||
encoder_has_internal_source_(false),
|
encoder_has_internal_source_(false),
|
||||||
next_frame_types_(1, kVideoFrameDelta) {
|
next_frame_types_(1, kVideoFrameDelta) {
|
||||||
_mediaOpt.Reset();
|
_mediaOpt.Reset();
|
||||||
@ -172,7 +175,7 @@ int VideoSender::Bitrate(unsigned int* bitrate) const {
|
|||||||
|
|
||||||
if (!_encoder)
|
if (!_encoder)
|
||||||
return VCM_UNINITIALIZED;
|
return VCM_UNINITIALIZED;
|
||||||
*bitrate = _encoder->GetEncoderParameters().target_bitrate;
|
*bitrate = _encoder->GetEncoderParameters().target_bitrate.get_sum_bps();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,15 +192,38 @@ int VideoSender::FrameRate(unsigned int* framerate) const {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate,
|
int32_t VideoSender::UpdateChannelParemeters(
|
||||||
uint8_t lossRate,
|
VideoBitrateAllocator* bitrate_allocator) {
|
||||||
int64_t rtt) {
|
EncoderParameters encoder_params;
|
||||||
uint32_t target_rate =
|
{
|
||||||
_mediaOpt.SetTargetRates(target_bitrate, lossRate, rtt);
|
rtc::CritScope cs(¶ms_crit_);
|
||||||
|
encoder_params = encoder_params_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SetChannelParameters(encoder_params.target_bitrate.get_sum_bps(),
|
||||||
|
encoder_params.loss_rate, encoder_params.rtt,
|
||||||
|
bitrate_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t VideoSender::SetChannelParameters(
|
||||||
|
uint32_t target_bitrate_bps,
|
||||||
|
uint8_t lossRate,
|
||||||
|
int64_t rtt,
|
||||||
|
VideoBitrateAllocator* bitrate_allocator) {
|
||||||
|
uint32_t video_target_rate_bps =
|
||||||
|
_mediaOpt.SetTargetRates(target_bitrate_bps, lossRate, rtt);
|
||||||
uint32_t input_frame_rate = _mediaOpt.InputFrameRate();
|
uint32_t input_frame_rate = _mediaOpt.InputFrameRate();
|
||||||
|
BitrateAllocation bitrate_allocation;
|
||||||
|
if (bitrate_allocator) {
|
||||||
|
bitrate_allocation = bitrate_allocator->GetAllocation(video_target_rate_bps,
|
||||||
|
input_frame_rate);
|
||||||
|
} else {
|
||||||
|
DefaultVideoBitrateAllocator default_allocator(current_codec_);
|
||||||
|
bitrate_allocation = default_allocator.GetAllocation(video_target_rate_bps,
|
||||||
|
input_frame_rate);
|
||||||
|
}
|
||||||
|
|
||||||
EncoderParameters encoder_params = {target_rate, lossRate, rtt,
|
EncoderParameters encoder_params = {bitrate_allocation, lossRate, rtt,
|
||||||
input_frame_rate};
|
input_frame_rate};
|
||||||
bool encoder_has_internal_source;
|
bool encoder_has_internal_source;
|
||||||
{
|
{
|
||||||
@ -228,7 +254,7 @@ void VideoSender::SetEncoderParameters(EncoderParameters params,
|
|||||||
// encoder implementations behave when given a zero target bitrate.
|
// encoder implementations behave when given a zero target bitrate.
|
||||||
// TODO(perkj): Make sure all known encoder implementations handle zero
|
// TODO(perkj): Make sure all known encoder implementations handle zero
|
||||||
// target bitrate and remove this check.
|
// target bitrate and remove this check.
|
||||||
if (!has_internal_source && params.target_bitrate == 0)
|
if (!has_internal_source && params.target_bitrate.get_sum_bps() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (params.input_frame_rate == 0) {
|
if (params.input_frame_rate == 0) {
|
||||||
@ -268,7 +294,8 @@ int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame,
|
|||||||
SetEncoderParameters(encoder_params, encoder_has_internal_source);
|
SetEncoderParameters(encoder_params, encoder_has_internal_source);
|
||||||
if (_mediaOpt.DropFrame()) {
|
if (_mediaOpt.DropFrame()) {
|
||||||
LOG(LS_VERBOSE) << "Drop Frame "
|
LOG(LS_VERBOSE) << "Drop Frame "
|
||||||
<< "target bitrate " << encoder_params.target_bitrate
|
<< "target bitrate "
|
||||||
|
<< encoder_params.target_bitrate.get_sum_bps()
|
||||||
<< " loss rate " << encoder_params.loss_rate << " rtt "
|
<< " loss rate " << encoder_params.loss_rate << " rtt "
|
||||||
<< encoder_params.rtt << " input frame rate "
|
<< encoder_params.rtt << " input frame rate "
|
||||||
<< encoder_params.input_frame_rate;
|
<< encoder_params.input_frame_rate;
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
#include "webrtc/modules/video_coding/include/video_coding.h"
|
#include "webrtc/modules/video_coding/include/video_coding.h"
|
||||||
#include "webrtc/modules/video_coding/test/test_util.h"
|
#include "webrtc/modules/video_coding/test/test_util.h"
|
||||||
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
#include "webrtc/system_wrappers/include/clock.h"
|
#include "webrtc/system_wrappers/include/clock.h"
|
||||||
#include "webrtc/test/frame_generator.h"
|
#include "webrtc/test/frame_generator.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
@ -196,6 +198,10 @@ class TestVideoSender : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class TestVideoSenderWithMockEncoder : public TestVideoSender {
|
class TestVideoSenderWithMockEncoder : public TestVideoSender {
|
||||||
|
public:
|
||||||
|
TestVideoSenderWithMockEncoder() {}
|
||||||
|
~TestVideoSenderWithMockEncoder() override {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
TestVideoSender::SetUp();
|
TestVideoSender::SetUp();
|
||||||
@ -212,6 +218,7 @@ class TestVideoSenderWithMockEncoder : public TestVideoSender {
|
|||||||
generator_.reset(
|
generator_.reset(
|
||||||
new EmptyFrameGenerator(settings_.width, settings_.height));
|
new EmptyFrameGenerator(settings_.width, settings_.height));
|
||||||
EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200));
|
EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200));
|
||||||
|
rate_allocator_.reset(new DefaultVideoBitrateAllocator(settings_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override { sender_.reset(); }
|
void TearDown() override { sender_.reset(); }
|
||||||
@ -261,6 +268,7 @@ class TestVideoSenderWithMockEncoder : public TestVideoSender {
|
|||||||
|
|
||||||
VideoCodec settings_;
|
VideoCodec settings_;
|
||||||
NiceMock<MockVideoEncoder> encoder_;
|
NiceMock<MockVideoEncoder> encoder_;
|
||||||
|
std::unique_ptr<DefaultVideoBitrateAllocator> rate_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequests) {
|
TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequests) {
|
||||||
@ -291,14 +299,19 @@ TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestVideoSenderWithMockEncoder, TestSetRate) {
|
TEST_F(TestVideoSenderWithMockEncoder, TestSetRate) {
|
||||||
const uint32_t new_bitrate = settings_.startBitrate + 300;
|
const uint32_t new_bitrate_kbps = settings_.startBitrate + 300;
|
||||||
EXPECT_CALL(encoder_, SetRates(new_bitrate, _)).Times(1).WillOnce(Return(0));
|
BitrateAllocation new_rate_allocation = rate_allocator_->GetAllocation(
|
||||||
sender_->SetChannelParameters(new_bitrate * 1000, 0, 200);
|
new_bitrate_kbps * 1000, settings_.maxFramerate);
|
||||||
|
EXPECT_CALL(encoder_, SetRateAllocation(new_rate_allocation, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Return(0));
|
||||||
|
sender_->SetChannelParameters(new_bitrate_kbps * 1000, 0, 200,
|
||||||
|
rate_allocator_.get());
|
||||||
AddFrame();
|
AddFrame();
|
||||||
|
|
||||||
// Expect no call to encoder_.SetRates if the new bitrate is zero.
|
// Expect no call to encoder_.SetRates if the new bitrate is zero.
|
||||||
EXPECT_CALL(encoder_, SetRates(new_bitrate, _)).Times(0);
|
EXPECT_CALL(encoder_, SetRateAllocation(_, _)).Times(0);
|
||||||
sender_->SetChannelParameters(0, 0, 200);
|
sender_->SetChannelParameters(0, 0, 200, rate_allocator_.get());
|
||||||
AddFrame();
|
AddFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,13 +342,19 @@ TEST_F(TestVideoSenderWithMockEncoder, TestEncoderParametersForInternalSource) {
|
|||||||
EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200));
|
EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200));
|
||||||
// Update encoder bitrate parameters. We expect that to immediately call
|
// Update encoder bitrate parameters. We expect that to immediately call
|
||||||
// SetRates on the encoder without waiting for AddFrame processing.
|
// SetRates on the encoder without waiting for AddFrame processing.
|
||||||
const uint32_t new_bitrate = settings_.startBitrate + 300;
|
const uint32_t new_bitrate_kbps = settings_.startBitrate + 300;
|
||||||
EXPECT_CALL(encoder_, SetRates(new_bitrate, _)).Times(1).WillOnce(Return(0));
|
BitrateAllocation new_rate_allocation = rate_allocator_->GetAllocation(
|
||||||
sender_->SetChannelParameters(new_bitrate * 1000, 0, 200);
|
new_bitrate_kbps * 1000, settings_.maxFramerate);
|
||||||
|
EXPECT_CALL(encoder_, SetRateAllocation(new_rate_allocation, _))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Return(0));
|
||||||
|
sender_->SetChannelParameters(new_bitrate_kbps * 1000, 0, 200,
|
||||||
|
rate_allocator_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestVideoSenderWithMockEncoder, EncoderFramerateUpdatedViaProcess) {
|
TEST_F(TestVideoSenderWithMockEncoder, EncoderFramerateUpdatedViaProcess) {
|
||||||
sender_->SetChannelParameters(settings_.startBitrate * 1000, 0, 200);
|
sender_->SetChannelParameters(settings_.startBitrate * 1000, 0, 200,
|
||||||
|
rate_allocator_.get());
|
||||||
const int64_t kRateStatsWindowMs = 2000;
|
const int64_t kRateStatsWindowMs = 2000;
|
||||||
const uint32_t kInputFps = 20;
|
const uint32_t kInputFps = 20;
|
||||||
int64_t start_time = clock_.TimeInMilliseconds();
|
int64_t start_time = clock_.TimeInMilliseconds();
|
||||||
@ -343,7 +362,9 @@ TEST_F(TestVideoSenderWithMockEncoder, EncoderFramerateUpdatedViaProcess) {
|
|||||||
AddFrame();
|
AddFrame();
|
||||||
clock_.AdvanceTimeMilliseconds(1000 / kInputFps);
|
clock_.AdvanceTimeMilliseconds(1000 / kInputFps);
|
||||||
}
|
}
|
||||||
EXPECT_CALL(encoder_, SetRates(_, kInputFps)).Times(1).WillOnce(Return(0));
|
EXPECT_CALL(encoder_, SetRateAllocation(_, kInputFps))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Return(0));
|
||||||
sender_->Process();
|
sender_->Process();
|
||||||
AddFrame();
|
AddFrame();
|
||||||
}
|
}
|
||||||
@ -361,23 +382,29 @@ TEST_F(TestVideoSenderWithMockEncoder,
|
|||||||
EXPECT_CALL(encoder_, SetChannelParameters(kLossRate, kRtt))
|
EXPECT_CALL(encoder_, SetChannelParameters(kLossRate, kRtt))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(Return(0));
|
.WillOnce(Return(0));
|
||||||
sender_->SetChannelParameters(settings_.startBitrate * 1000, kLossRate, kRtt);
|
sender_->SetChannelParameters(settings_.startBitrate * 1000, kLossRate, kRtt,
|
||||||
|
rate_allocator_.get());
|
||||||
while (clock_.TimeInMilliseconds() < start_time + kRateStatsWindowMs) {
|
while (clock_.TimeInMilliseconds() < start_time + kRateStatsWindowMs) {
|
||||||
AddFrame();
|
AddFrame();
|
||||||
clock_.AdvanceTimeMilliseconds(1000 / kInputFps);
|
clock_.AdvanceTimeMilliseconds(1000 / kInputFps);
|
||||||
}
|
}
|
||||||
// After process, input framerate should be updated but not ChannelParameters
|
// After process, input framerate should be updated but not ChannelParameters
|
||||||
// as they are the same as before.
|
// as they are the same as before.
|
||||||
EXPECT_CALL(encoder_, SetRates(_, kInputFps)).Times(1).WillOnce(Return(0));
|
EXPECT_CALL(encoder_, SetRateAllocation(_, kInputFps))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Return(0));
|
||||||
sender_->Process();
|
sender_->Process();
|
||||||
AddFrame();
|
AddFrame();
|
||||||
// Call to SetChannelParameters with changed bitrate should call encoder
|
// Call to SetChannelParameters with changed bitrate should call encoder
|
||||||
// SetRates but not encoder SetChannelParameters (that are unchanged).
|
// SetRates but not encoder SetChannelParameters (that are unchanged).
|
||||||
EXPECT_CALL(encoder_, SetRates(2 * settings_.startBitrate, kInputFps))
|
uint32_t new_bitrate_bps = 2 * settings_.startBitrate * 1000;
|
||||||
|
BitrateAllocation new_rate_allocation =
|
||||||
|
rate_allocator_->GetAllocation(new_bitrate_bps, kInputFps);
|
||||||
|
EXPECT_CALL(encoder_, SetRateAllocation(new_rate_allocation, kInputFps))
|
||||||
.Times(1)
|
.Times(1)
|
||||||
.WillOnce(Return(0));
|
.WillOnce(Return(0));
|
||||||
sender_->SetChannelParameters(2 * settings_.startBitrate * 1000, kLossRate,
|
sender_->SetChannelParameters(new_bitrate_bps, kLossRate, kRtt,
|
||||||
kRtt);
|
rate_allocator_.get());
|
||||||
AddFrame();
|
AddFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,6 +427,12 @@ class TestVideoSenderWithVp8 : public TestVideoSender {
|
|||||||
codec_.minBitrate = 10;
|
codec_.minBitrate = 10;
|
||||||
codec_.startBitrate = codec_bitrate_kbps_;
|
codec_.startBitrate = codec_bitrate_kbps_;
|
||||||
codec_.maxBitrate = codec_bitrate_kbps_;
|
codec_.maxBitrate = codec_bitrate_kbps_;
|
||||||
|
|
||||||
|
TemporalLayersFactory* tl_factory = new TemporalLayersFactory();
|
||||||
|
rate_allocator_.reset(new SimulcastRateAllocator(
|
||||||
|
codec_, std::unique_ptr<TemporalLayersFactory>(tl_factory)));
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = tl_factory;
|
||||||
|
|
||||||
encoder_.reset(VP8Encoder::Create());
|
encoder_.reset(VP8Encoder::Create());
|
||||||
sender_->RegisterExternalEncoder(encoder_.get(), codec_.plType, false);
|
sender_->RegisterExternalEncoder(encoder_.get(), codec_.plType, false);
|
||||||
EXPECT_EQ(0, sender_->RegisterSendCodec(&codec_, 1, 1200));
|
EXPECT_EQ(0, sender_->RegisterSendCodec(&codec_, 1, 1200));
|
||||||
@ -425,8 +458,9 @@ class TestVideoSenderWithVp8 : public TestVideoSender {
|
|||||||
// Note: SetChannelParameters fails if less than 2 frames are in the
|
// Note: SetChannelParameters fails if less than 2 frames are in the
|
||||||
// buffer since it will fail to calculate the framerate.
|
// buffer since it will fail to calculate the framerate.
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
EXPECT_EQ(VCM_OK, sender_->SetChannelParameters(
|
EXPECT_EQ(VCM_OK,
|
||||||
available_bitrate_kbps_ * 1000, 0, 200));
|
sender_->SetChannelParameters(available_bitrate_kbps_ * 1000,
|
||||||
|
0, 200, rate_allocator_.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,6 +481,7 @@ class TestVideoSenderWithVp8 : public TestVideoSender {
|
|||||||
VideoCodec codec_;
|
VideoCodec codec_;
|
||||||
int codec_bitrate_kbps_;
|
int codec_bitrate_kbps_;
|
||||||
int available_bitrate_kbps_;
|
int available_bitrate_kbps_;
|
||||||
|
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
|
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
|
||||||
@ -476,11 +511,15 @@ TEST_F(TestVideoSenderWithVp8, MAYBE_FixedTemporalLayersStrategy) {
|
|||||||
#endif
|
#endif
|
||||||
TEST_F(TestVideoSenderWithVp8, MAYBE_RealTimeTemporalLayersStrategy) {
|
TEST_F(TestVideoSenderWithVp8, MAYBE_RealTimeTemporalLayersStrategy) {
|
||||||
VideoCodec codec = MakeVp8VideoCodec(352, 288, 3);
|
VideoCodec codec = MakeVp8VideoCodec(352, 288, 3);
|
||||||
RealTimeTemporalLayersFactory realtime_tl_factory;
|
|
||||||
codec.VP8()->tl_factory = &realtime_tl_factory;
|
|
||||||
codec.minBitrate = 10;
|
codec.minBitrate = 10;
|
||||||
codec.startBitrate = codec_bitrate_kbps_;
|
codec.startBitrate = codec_bitrate_kbps_;
|
||||||
codec.maxBitrate = codec_bitrate_kbps_;
|
codec.maxBitrate = codec_bitrate_kbps_;
|
||||||
|
|
||||||
|
TemporalLayersFactory* tl_factory = new RealTimeTemporalLayersFactory();
|
||||||
|
rate_allocator_.reset(new SimulcastRateAllocator(
|
||||||
|
codec, std::unique_ptr<TemporalLayersFactory>(tl_factory)));
|
||||||
|
codec.VP8()->tl_factory = tl_factory;
|
||||||
|
|
||||||
EXPECT_EQ(0, sender_->RegisterSendCodec(&codec, 1, 1200));
|
EXPECT_EQ(0, sender_->RegisterSendCodec(&codec, 1, 1200));
|
||||||
|
|
||||||
const int low_b = codec_bitrate_kbps_ * 0.4;
|
const int low_b = codec_bitrate_kbps_ * 0.4;
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/common_video/include/video_image.h"
|
#include "webrtc/common_video/include/video_image.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
@ -72,8 +73,9 @@ int32_t ConfigurableFrameSizeEncoder::SetChannelParameters(uint32_t packet_loss,
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ConfigurableFrameSizeEncoder::SetRates(uint32_t new_bit_rate,
|
int32_t ConfigurableFrameSizeEncoder::SetRateAllocation(
|
||||||
uint32_t frame_rate) {
|
const BitrateAllocation& allocation,
|
||||||
|
uint32_t framerate) {
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +84,7 @@ int32_t ConfigurableFrameSizeEncoder::SetPeriodicKeyFrames(bool enable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t ConfigurableFrameSizeEncoder::SetFrameSize(size_t size) {
|
int32_t ConfigurableFrameSizeEncoder::SetFrameSize(size_t size) {
|
||||||
assert(size <= max_frame_size_);
|
RTC_DCHECK_LE(size, max_frame_size_);
|
||||||
current_frame_size_ = size;
|
current_frame_size_ = size;
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,8 @@ class ConfigurableFrameSizeEncoder : public VideoEncoder {
|
|||||||
|
|
||||||
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) 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 BitrateAllocation& allocation,
|
||||||
|
uint32_t framerate) override;
|
||||||
|
|
||||||
int32_t SetPeriodicKeyFrames(bool enable) override;
|
int32_t SetPeriodicKeyFrames(bool enable) override;
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,6 @@ namespace test {
|
|||||||
FakeEncoder::FakeEncoder(Clock* clock)
|
FakeEncoder::FakeEncoder(Clock* clock)
|
||||||
: clock_(clock),
|
: clock_(clock),
|
||||||
callback_(NULL),
|
callback_(NULL),
|
||||||
target_bitrate_kbps_(0),
|
|
||||||
max_target_bitrate_kbps_(-1),
|
max_target_bitrate_kbps_(-1),
|
||||||
last_encode_time_ms_(0) {
|
last_encode_time_ms_(0) {
|
||||||
// Generate some arbitrary not-all-zero data
|
// Generate some arbitrary not-all-zero data
|
||||||
@ -43,7 +42,7 @@ int32_t FakeEncoder::InitEncode(const VideoCodec* config,
|
|||||||
int32_t number_of_cores,
|
int32_t number_of_cores,
|
||||||
size_t max_payload_size) {
|
size_t max_payload_size) {
|
||||||
config_ = *config;
|
config_ = *config;
|
||||||
target_bitrate_kbps_ = config_.startBitrate;
|
target_bitrate_.SetBitrate(0, 0, config_.startBitrate * 1000);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,8 +64,8 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
|
|||||||
time_since_last_encode_ms = 3 * 1000 / config_.maxFramerate;
|
time_since_last_encode_ms = 3 * 1000 / config_.maxFramerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bits_available =
|
size_t bits_available = static_cast<size_t>(target_bitrate_.get_sum_kbps() *
|
||||||
static_cast<size_t>(target_bitrate_kbps_ * time_since_last_encode_ms);
|
time_since_last_encode_ms);
|
||||||
size_t min_bits = static_cast<size_t>(
|
size_t min_bits = static_cast<size_t>(
|
||||||
config_.simulcastStream[0].minBitrate * time_since_last_encode_ms);
|
config_.simulcastStream[0].minBitrate * time_since_last_encode_ms);
|
||||||
if (bits_available < min_bits)
|
if (bits_available < min_bits)
|
||||||
@ -133,8 +132,9 @@ int32_t FakeEncoder::SetChannelParameters(uint32_t packet_loss, int64_t rtt) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t FakeEncoder::SetRates(uint32_t new_target_bitrate, uint32_t framerate) {
|
int32_t FakeEncoder::SetRateAllocation(const BitrateAllocation& rate_allocation,
|
||||||
target_bitrate_kbps_ = new_target_bitrate;
|
uint32_t framerate) {
|
||||||
|
target_bitrate_ = rate_allocation;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,8 @@ class FakeEncoder : public VideoEncoder {
|
|||||||
EncodedImageCallback* callback) override;
|
EncodedImageCallback* callback) override;
|
||||||
int32_t Release() override;
|
int32_t Release() override;
|
||||||
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
||||||
int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) override;
|
int32_t SetRateAllocation(const BitrateAllocation& rate_allocation,
|
||||||
|
uint32_t framerate) override;
|
||||||
const char* ImplementationName() const override;
|
const char* ImplementationName() const override;
|
||||||
|
|
||||||
static const char* kImplementationName;
|
static const char* kImplementationName;
|
||||||
@ -48,7 +49,7 @@ class FakeEncoder : public VideoEncoder {
|
|||||||
Clock* const clock_;
|
Clock* const clock_;
|
||||||
VideoCodec config_;
|
VideoCodec config_;
|
||||||
EncodedImageCallback* callback_;
|
EncodedImageCallback* callback_;
|
||||||
int target_bitrate_kbps_;
|
BitrateAllocation target_bitrate_;
|
||||||
int max_target_bitrate_kbps_;
|
int max_target_bitrate_kbps_;
|
||||||
int64_t last_encode_time_ms_;
|
int64_t last_encode_time_ms_;
|
||||||
uint8_t encoded_buffer_[100000];
|
uint8_t encoded_buffer_[100000];
|
||||||
|
|||||||
@ -2550,12 +2550,13 @@ TEST_F(EndToEndTest, ReportsSetEncoderRates) {
|
|||||||
RTC_DCHECK_EQ(1u, encoder_config->number_of_streams);
|
RTC_DCHECK_EQ(1u, encoder_config->number_of_streams);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) override {
|
int32_t SetRateAllocation(const BitrateAllocation& rate_allocation,
|
||||||
|
uint32_t framerate) override {
|
||||||
// Make sure not to trigger on any default zero bitrates.
|
// Make sure not to trigger on any default zero bitrates.
|
||||||
if (new_target_bitrate == 0)
|
if (rate_allocation.get_sum_bps() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
bitrate_kbps_ = new_target_bitrate;
|
bitrate_kbps_ = rate_allocation.get_sum_kbps();
|
||||||
observation_complete_.Set();
|
observation_complete_.Set();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,8 +67,13 @@ VideoEncoder::EncoderType VideoEncoder::CodecToEncoderType(
|
|||||||
VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
|
VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
|
||||||
VideoCodecType codec_type,
|
VideoCodecType codec_type,
|
||||||
webrtc::VideoEncoder* encoder)
|
webrtc::VideoEncoder* encoder)
|
||||||
: rates_set_(false),
|
: number_of_cores_(0),
|
||||||
|
max_payload_size_(0),
|
||||||
|
rates_set_(false),
|
||||||
|
framerate_(0),
|
||||||
channel_parameters_set_(false),
|
channel_parameters_set_(false),
|
||||||
|
packet_loss_(0),
|
||||||
|
rtt_(0),
|
||||||
encoder_type_(CodecToEncoderType(codec_type)),
|
encoder_type_(CodecToEncoderType(codec_type)),
|
||||||
encoder_(encoder),
|
encoder_(encoder),
|
||||||
callback_(nullptr) {}
|
callback_(nullptr) {}
|
||||||
@ -92,7 +97,7 @@ bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
|
|||||||
if (callback_)
|
if (callback_)
|
||||||
fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
|
fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
|
||||||
if (rates_set_)
|
if (rates_set_)
|
||||||
fallback_encoder_->SetRates(bitrate_, framerate_);
|
fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
|
||||||
if (channel_parameters_set_)
|
if (channel_parameters_set_)
|
||||||
fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
|
fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
|
||||||
|
|
||||||
@ -189,14 +194,15 @@ int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t VideoEncoderSoftwareFallbackWrapper::SetRates(uint32_t bitrate,
|
int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
|
||||||
uint32_t framerate) {
|
const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t framerate) {
|
||||||
rates_set_ = true;
|
rates_set_ = true;
|
||||||
bitrate_ = bitrate;
|
bitrate_allocation_ = bitrate_allocation;
|
||||||
framerate_ = framerate;
|
framerate_ = framerate;
|
||||||
int32_t ret = encoder_->SetRates(bitrate, framerate);
|
int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
|
||||||
if (fallback_encoder_)
|
if (fallback_encoder_)
|
||||||
return fallback_encoder_->SetRates(bitrate, framerate);
|
return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,11 @@
|
|||||||
|
|
||||||
#include "webrtc/video_encoder.h"
|
#include "webrtc/video_encoder.h"
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_error_codes.h"
|
#include "webrtc/modules/video_coding/include/video_error_codes.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -63,7 +66,8 @@ class VideoEncoderSoftwareFallbackWrapperTest : public ::testing::Test {
|
|||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SetRates(uint32_t bitrate, uint32_t framerate) override {
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
|
||||||
|
uint32_t framerate) override {
|
||||||
++set_rates_count_;
|
++set_rates_count_;
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
@ -117,6 +121,7 @@ class VideoEncoderSoftwareFallbackWrapperTest : public ::testing::Test {
|
|||||||
VideoEncoderSoftwareFallbackWrapper fallback_wrapper_;
|
VideoEncoderSoftwareFallbackWrapper fallback_wrapper_;
|
||||||
VideoCodec codec_ = {};
|
VideoCodec codec_ = {};
|
||||||
std::unique_ptr<VideoFrame> frame_;
|
std::unique_ptr<VideoFrame> frame_;
|
||||||
|
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void VideoEncoderSoftwareFallbackWrapperTest::EncodeFrame() {
|
void VideoEncoderSoftwareFallbackWrapperTest::EncodeFrame() {
|
||||||
@ -139,10 +144,19 @@ void VideoEncoderSoftwareFallbackWrapperTest::UtilizeFallbackEncoder() {
|
|||||||
codec_.maxFramerate = 30;
|
codec_.maxFramerate = 30;
|
||||||
codec_.width = kWidth;
|
codec_.width = kWidth;
|
||||||
codec_.height = kHeight;
|
codec_.height = kHeight;
|
||||||
|
codec_.codecSpecific.VP8.numberOfTemporalLayers = 1;
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory(
|
||||||
|
new TemporalLayersFactory());
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = tl_factory.get();
|
||||||
|
rate_allocator_.reset(
|
||||||
|
new SimulcastRateAllocator(codec_, std::move(tl_factory)));
|
||||||
|
|
||||||
fake_encoder_.init_encode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
|
fake_encoder_.init_encode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||||
fallback_wrapper_.InitEncode(&codec_, 2, kMaxPayloadSize));
|
fallback_wrapper_.InitEncode(&codec_, 2, kMaxPayloadSize));
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.SetRates(300, 30));
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||||
|
fallback_wrapper_.SetRateAllocation(
|
||||||
|
rate_allocator_->GetAllocation(300000, 30), 30));
|
||||||
|
|
||||||
int callback_count = callback_.callback_count_;
|
int callback_count = callback_.callback_count_;
|
||||||
int encode_count = fake_encoder_.encode_count_;
|
int encode_count = fake_encoder_.encode_count_;
|
||||||
@ -157,8 +171,16 @@ void VideoEncoderSoftwareFallbackWrapperTest::FallbackFromEncodeRequest() {
|
|||||||
codec_.maxFramerate = 30;
|
codec_.maxFramerate = 30;
|
||||||
codec_.width = kWidth;
|
codec_.width = kWidth;
|
||||||
codec_.height = kHeight;
|
codec_.height = kHeight;
|
||||||
|
codec_.codecSpecific.VP8.numberOfTemporalLayers = 1;
|
||||||
|
std::unique_ptr<TemporalLayersFactory> tl_factory(
|
||||||
|
new TemporalLayersFactory());
|
||||||
|
codec_.codecSpecific.VP8.tl_factory = tl_factory.get();
|
||||||
|
rate_allocator_.reset(
|
||||||
|
new SimulcastRateAllocator(codec_, std::move(tl_factory)));
|
||||||
fallback_wrapper_.InitEncode(&codec_, 2, kMaxPayloadSize);
|
fallback_wrapper_.InitEncode(&codec_, 2, kMaxPayloadSize);
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.SetRates(300, 30));
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||||
|
fallback_wrapper_.SetRateAllocation(
|
||||||
|
rate_allocator_->GetAllocation(300000, 30), 30));
|
||||||
EXPECT_EQ(1, fake_encoder_.init_encode_count_);
|
EXPECT_EQ(1, fake_encoder_.init_encode_count_);
|
||||||
|
|
||||||
// Have the non-fallback encoder request a software fallback.
|
// Have the non-fallback encoder request a software fallback.
|
||||||
@ -241,7 +263,7 @@ TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
|
|||||||
SetRatesForwardedDuringFallback) {
|
SetRatesForwardedDuringFallback) {
|
||||||
UtilizeFallbackEncoder();
|
UtilizeFallbackEncoder();
|
||||||
EXPECT_EQ(1, fake_encoder_.set_rates_count_);
|
EXPECT_EQ(1, fake_encoder_.set_rates_count_);
|
||||||
fallback_wrapper_.SetRates(1, 1);
|
fallback_wrapper_.SetRateAllocation(BitrateAllocation(), 1);
|
||||||
EXPECT_EQ(2, fake_encoder_.set_rates_count_);
|
EXPECT_EQ(2, fake_encoder_.set_rates_count_);
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.Release());
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.Release());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1478,7 +1478,8 @@ TEST_F(VideoSendStreamTest,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_encode_called_.Wait(VideoSendStreamTest::kDefaultTimeoutMs);
|
EXPECT_TRUE(
|
||||||
|
init_encode_called_.Wait(VideoSendStreamTest::kDefaultTimeoutMs));
|
||||||
{
|
{
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
EXPECT_EQ(width, last_initialized_frame_width_);
|
EXPECT_EQ(width, last_initialized_frame_width_);
|
||||||
@ -1494,9 +1495,7 @@ TEST_F(VideoSendStreamTest,
|
|||||||
last_initialized_frame_width_ = config->width;
|
last_initialized_frame_width_ = config->width;
|
||||||
last_initialized_frame_height_ = config->height;
|
last_initialized_frame_height_ = config->height;
|
||||||
++number_of_initializations_;
|
++number_of_initializations_;
|
||||||
// First time InitEncode is called, the frame size is unknown.
|
init_encode_called_.Set();
|
||||||
if (number_of_initializations_ > 1)
|
|
||||||
init_encode_called_.Set();
|
|
||||||
return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
|
return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1626,11 +1625,12 @@ TEST_F(VideoSendStreamTest, VideoSendStreamStopSetEncoderRateToZero) {
|
|||||||
return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
|
return FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) override {
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
|
uint32_t framerate) override {
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
bitrate_kbps_ = new_target_bitrate;
|
bitrate_kbps_ = bitrate.get_sum_kbps();
|
||||||
bitrate_changed_.Set();
|
bitrate_changed_.Set();
|
||||||
return FakeEncoder::SetRates(new_target_bitrate, framerate);
|
return FakeEncoder::SetRateAllocation(bitrate, framerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetBitrateKbps() const {
|
int GetBitrateKbps() const {
|
||||||
@ -1664,13 +1664,17 @@ TEST_F(VideoSendStreamTest, VideoSendStreamStopSetEncoderRateToZero) {
|
|||||||
CreateVideoStreams();
|
CreateVideoStreams();
|
||||||
|
|
||||||
EXPECT_TRUE(encoder.WaitForEncoderInit());
|
EXPECT_TRUE(encoder.WaitForEncoderInit());
|
||||||
EXPECT_GT(encoder.GetBitrateKbps(), 0);
|
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
||||||
|
EXPECT_EQ(0, encoder.GetBitrateKbps());
|
||||||
|
|
||||||
video_send_stream_->Start();
|
video_send_stream_->Start();
|
||||||
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
||||||
EXPECT_GT(encoder.GetBitrateKbps(), 0);
|
EXPECT_GT(encoder.GetBitrateKbps(), 0);
|
||||||
|
|
||||||
video_send_stream_->Stop();
|
video_send_stream_->Stop();
|
||||||
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
||||||
EXPECT_EQ(0, encoder.GetBitrateKbps());
|
EXPECT_EQ(0, encoder.GetBitrateKbps());
|
||||||
|
|
||||||
video_send_stream_->Start();
|
video_send_stream_->Start();
|
||||||
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
EXPECT_TRUE(encoder.WaitBitrateChanged());
|
||||||
EXPECT_GT(encoder.GetBitrateKbps(), 0);
|
EXPECT_GT(encoder.GetBitrateKbps(), 0);
|
||||||
@ -2085,10 +2089,12 @@ void VideoCodecConfigObserver<VideoCodecVP8>::VerifyCodecSpecifics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set expected temporal layers as they should have been set when
|
// Set expected temporal layers as they should have been set when
|
||||||
// reconfiguring the encoder and not match the set config.
|
// reconfiguring the encoder and not match the set config. Also copy the
|
||||||
|
// TemporalLayersFactory pointer that has been injected by ViEEncoder.
|
||||||
VideoCodecVP8 encoder_settings = encoder_settings_;
|
VideoCodecVP8 encoder_settings = encoder_settings_;
|
||||||
encoder_settings.numberOfTemporalLayers =
|
encoder_settings.numberOfTemporalLayers =
|
||||||
kVideoCodecConfigObserverNumberOfTemporalLayers;
|
kVideoCodecConfigObserverNumberOfTemporalLayers;
|
||||||
|
encoder_settings.tl_factory = config.VP8().tl_factory;
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
0, memcmp(&config.VP8(), &encoder_settings, sizeof(encoder_settings_)));
|
0, memcmp(&config.VP8(), &encoder_settings, sizeof(encoder_settings_)));
|
||||||
}
|
}
|
||||||
@ -2310,16 +2316,17 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
|
|||||||
maxPayloadSize);
|
maxPayloadSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) override {
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate,
|
||||||
|
uint32_t frameRate) override {
|
||||||
{
|
{
|
||||||
rtc::CritScope lock(&crit_);
|
rtc::CritScope lock(&crit_);
|
||||||
if (target_bitrate_ == newBitRate) {
|
if (target_bitrate_ == bitrate.get_sum_kbps()) {
|
||||||
return FakeEncoder::SetRates(newBitRate, frameRate);
|
return FakeEncoder::SetRateAllocation(bitrate, frameRate);
|
||||||
}
|
}
|
||||||
target_bitrate_ = newBitRate;
|
target_bitrate_ = bitrate.get_sum_kbps();
|
||||||
}
|
}
|
||||||
bitrate_changed_event_.Set();
|
bitrate_changed_event_.Set();
|
||||||
return FakeEncoder::SetRates(newBitRate, frameRate);
|
return FakeEncoder::SetRateAllocation(bitrate, frameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitForSetRates(uint32_t expected_bitrate) {
|
void WaitForSetRates(uint32_t expected_bitrate) {
|
||||||
|
|||||||
@ -14,11 +14,13 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
|
||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/base/logging.h"
|
#include "webrtc/base/logging.h"
|
||||||
#include "webrtc/base/trace_event.h"
|
#include "webrtc/base/trace_event.h"
|
||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/modules/pacing/paced_sender.h"
|
#include "webrtc/modules/pacing/paced_sender.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_coding.h"
|
#include "webrtc/modules/video_coding/include/video_coding.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
||||||
#include "webrtc/video/overuse_frame_detector.h"
|
#include "webrtc/video/overuse_frame_detector.h"
|
||||||
@ -31,148 +33,6 @@ namespace {
|
|||||||
// Time interval for logging frame counts.
|
// Time interval for logging frame counts.
|
||||||
const int64_t kFrameLogIntervalMs = 60000;
|
const int64_t kFrameLogIntervalMs = 60000;
|
||||||
|
|
||||||
VideoCodecType PayloadNameToCodecType(const std::string& payload_name) {
|
|
||||||
if (payload_name == "VP8")
|
|
||||||
return kVideoCodecVP8;
|
|
||||||
if (payload_name == "VP9")
|
|
||||||
return kVideoCodecVP9;
|
|
||||||
if (payload_name == "H264")
|
|
||||||
return kVideoCodecH264;
|
|
||||||
return kVideoCodecGeneric;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCodec VideoEncoderConfigToVideoCodec(
|
|
||||||
const VideoEncoderConfig& config,
|
|
||||||
const std::vector<VideoStream>& streams,
|
|
||||||
const std::string& payload_name,
|
|
||||||
int payload_type) {
|
|
||||||
static const int kEncoderMinBitrateKbps = 30;
|
|
||||||
RTC_DCHECK(!streams.empty());
|
|
||||||
RTC_DCHECK_GE(config.min_transmit_bitrate_bps, 0);
|
|
||||||
|
|
||||||
VideoCodec video_codec;
|
|
||||||
memset(&video_codec, 0, sizeof(video_codec));
|
|
||||||
video_codec.codecType = PayloadNameToCodecType(payload_name);
|
|
||||||
|
|
||||||
switch (config.content_type) {
|
|
||||||
case VideoEncoderConfig::ContentType::kRealtimeVideo:
|
|
||||||
video_codec.mode = kRealtimeVideo;
|
|
||||||
break;
|
|
||||||
case VideoEncoderConfig::ContentType::kScreen:
|
|
||||||
video_codec.mode = kScreensharing;
|
|
||||||
if (streams.size() == 1 &&
|
|
||||||
streams[0].temporal_layer_thresholds_bps.size() == 1) {
|
|
||||||
video_codec.targetBitrate =
|
|
||||||
streams[0].temporal_layer_thresholds_bps[0] / 1000;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.encoder_specific_settings)
|
|
||||||
config.encoder_specific_settings->FillEncoderSpecificSettings(&video_codec);
|
|
||||||
|
|
||||||
switch (video_codec.codecType) {
|
|
||||||
case kVideoCodecVP8: {
|
|
||||||
if (!config.encoder_specific_settings)
|
|
||||||
video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
|
|
||||||
video_codec.codecSpecific.VP8.numberOfTemporalLayers =
|
|
||||||
static_cast<unsigned char>(
|
|
||||||
streams.back().temporal_layer_thresholds_bps.size() + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case kVideoCodecVP9: {
|
|
||||||
if (!config.encoder_specific_settings)
|
|
||||||
video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
|
|
||||||
if (video_codec.mode == kScreensharing &&
|
|
||||||
config.encoder_specific_settings) {
|
|
||||||
video_codec.codecSpecific.VP9.flexibleMode = true;
|
|
||||||
// For now VP9 screensharing use 1 temporal and 2 spatial layers.
|
|
||||||
RTC_DCHECK_EQ(1, video_codec.codecSpecific.VP9.numberOfTemporalLayers);
|
|
||||||
RTC_DCHECK_EQ(2, video_codec.codecSpecific.VP9.numberOfSpatialLayers);
|
|
||||||
}
|
|
||||||
video_codec.codecSpecific.VP9.numberOfTemporalLayers =
|
|
||||||
static_cast<unsigned char>(
|
|
||||||
streams.back().temporal_layer_thresholds_bps.size() + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case kVideoCodecH264: {
|
|
||||||
if (!config.encoder_specific_settings)
|
|
||||||
video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO(pbos): Support encoder_settings codec-agnostically.
|
|
||||||
RTC_DCHECK(!config.encoder_specific_settings)
|
|
||||||
<< "Encoder-specific settings for codec type not wired up.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(video_codec.plName, payload_name.c_str(), kPayloadNameSize - 1);
|
|
||||||
video_codec.plName[kPayloadNameSize - 1] = '\0';
|
|
||||||
video_codec.plType = payload_type;
|
|
||||||
video_codec.numberOfSimulcastStreams =
|
|
||||||
static_cast<unsigned char>(streams.size());
|
|
||||||
video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
|
|
||||||
if (video_codec.minBitrate < kEncoderMinBitrateKbps)
|
|
||||||
video_codec.minBitrate = kEncoderMinBitrateKbps;
|
|
||||||
RTC_DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
|
|
||||||
if (video_codec.codecType == kVideoCodecVP9) {
|
|
||||||
// If the vector is empty, bitrates will be configured automatically.
|
|
||||||
RTC_DCHECK(config.spatial_layers.empty() ||
|
|
||||||
config.spatial_layers.size() ==
|
|
||||||
video_codec.codecSpecific.VP9.numberOfSpatialLayers);
|
|
||||||
RTC_DCHECK_LE(video_codec.codecSpecific.VP9.numberOfSpatialLayers,
|
|
||||||
kMaxSimulcastStreams);
|
|
||||||
for (size_t i = 0; i < config.spatial_layers.size(); ++i)
|
|
||||||
video_codec.spatialLayers[i] = config.spatial_layers[i];
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < streams.size(); ++i) {
|
|
||||||
SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
|
|
||||||
RTC_DCHECK_GT(streams[i].width, 0u);
|
|
||||||
RTC_DCHECK_GT(streams[i].height, 0u);
|
|
||||||
RTC_DCHECK_GT(streams[i].max_framerate, 0);
|
|
||||||
// Different framerates not supported per stream at the moment.
|
|
||||||
RTC_DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
|
|
||||||
RTC_DCHECK_GE(streams[i].min_bitrate_bps, 0);
|
|
||||||
RTC_DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
|
|
||||||
RTC_DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
|
|
||||||
RTC_DCHECK_GE(streams[i].max_qp, 0);
|
|
||||||
|
|
||||||
sim_stream->width = static_cast<uint16_t>(streams[i].width);
|
|
||||||
sim_stream->height = static_cast<uint16_t>(streams[i].height);
|
|
||||||
sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
|
|
||||||
sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
|
|
||||||
sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
|
|
||||||
sim_stream->qpMax = streams[i].max_qp;
|
|
||||||
sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
|
|
||||||
streams[i].temporal_layer_thresholds_bps.size() + 1);
|
|
||||||
|
|
||||||
video_codec.width = std::max(video_codec.width,
|
|
||||||
static_cast<uint16_t>(streams[i].width));
|
|
||||||
video_codec.height = std::max(
|
|
||||||
video_codec.height, static_cast<uint16_t>(streams[i].height));
|
|
||||||
video_codec.minBitrate =
|
|
||||||
std::min(static_cast<uint16_t>(video_codec.minBitrate),
|
|
||||||
static_cast<uint16_t>(streams[i].min_bitrate_bps / 1000));
|
|
||||||
video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
|
|
||||||
video_codec.qpMax = std::max(video_codec.qpMax,
|
|
||||||
static_cast<unsigned int>(streams[i].max_qp));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video_codec.maxBitrate == 0) {
|
|
||||||
// Unset max bitrate -> cap to one bit per pixel.
|
|
||||||
video_codec.maxBitrate =
|
|
||||||
(video_codec.width * video_codec.height * video_codec.maxFramerate) /
|
|
||||||
1000;
|
|
||||||
}
|
|
||||||
if (video_codec.maxBitrate < kEncoderMinBitrateKbps)
|
|
||||||
video_codec.maxBitrate = kEncoderMinBitrateKbps;
|
|
||||||
|
|
||||||
RTC_DCHECK_GT(streams[0].max_framerate, 0);
|
|
||||||
video_codec.maxFramerate = streams[0].max_framerate;
|
|
||||||
return video_codec;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
|
// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
|
||||||
// pipelining encoders better (multiple input frames before something comes
|
// pipelining encoders better (multiple input frames before something comes
|
||||||
// out). This should effectively turn off CPU adaptations for systems that
|
// out). This should effectively turn off CPU adaptations for systems that
|
||||||
@ -372,7 +232,8 @@ ViEEncoder::ViEEncoder(uint32_t number_of_cores,
|
|||||||
source_proxy_(new VideoSourceProxy(this)),
|
source_proxy_(new VideoSourceProxy(this)),
|
||||||
sink_(nullptr),
|
sink_(nullptr),
|
||||||
settings_(settings),
|
settings_(settings),
|
||||||
codec_type_(PayloadNameToCodecType(settings.payload_name)),
|
codec_type_(PayloadNameToCodecType(settings.payload_name)
|
||||||
|
.value_or(VideoCodecType::kVideoCodecUnknown)),
|
||||||
video_sender_(Clock::GetRealTimeClock(), this, this),
|
video_sender_(Clock::GetRealTimeClock(), this, this),
|
||||||
overuse_detector_(Clock::GetRealTimeClock(),
|
overuse_detector_(Clock::GetRealTimeClock(),
|
||||||
GetCpuOveruseOptions(settings.full_overuse_time),
|
GetCpuOveruseOptions(settings.full_overuse_time),
|
||||||
@ -424,6 +285,7 @@ void ViEEncoder::Stop() {
|
|||||||
encoder_queue_.PostTask([this] {
|
encoder_queue_.PostTask([this] {
|
||||||
RTC_DCHECK_RUN_ON(&encoder_queue_);
|
RTC_DCHECK_RUN_ON(&encoder_queue_);
|
||||||
overuse_detector_.StopCheckForOveruse();
|
overuse_detector_.StopCheckForOveruse();
|
||||||
|
rate_allocator_.reset();
|
||||||
video_sender_.RegisterExternalEncoder(nullptr, settings_.payload_type,
|
video_sender_.RegisterExternalEncoder(nullptr, settings_.payload_type,
|
||||||
false);
|
false);
|
||||||
shutdown_event_.Set();
|
shutdown_event_.Set();
|
||||||
@ -518,8 +380,11 @@ void ViEEncoder::ReconfigureEncoder() {
|
|||||||
encoder_config_.video_stream_factory->CreateEncoderStreams(
|
encoder_config_.video_stream_factory->CreateEncoderStreams(
|
||||||
last_frame_info_->width, last_frame_info_->height, encoder_config_);
|
last_frame_info_->width, last_frame_info_->height, encoder_config_);
|
||||||
|
|
||||||
VideoCodec codec = VideoEncoderConfigToVideoCodec(
|
VideoCodec codec;
|
||||||
encoder_config_, streams, settings_.payload_name, settings_.payload_type);
|
if (!VideoCodecInitializer::SetupCodec(encoder_config_, settings_, streams,
|
||||||
|
&codec, &rate_allocator_)) {
|
||||||
|
LOG(LS_ERROR) << "Failed to create encoder configuration.";
|
||||||
|
}
|
||||||
|
|
||||||
codec.startBitrate =
|
codec.startBitrate =
|
||||||
std::max(encoder_start_bitrate_bps_ / 1000, codec.minBitrate);
|
std::max(encoder_start_bitrate_bps_ / 1000, codec.minBitrate);
|
||||||
@ -534,17 +399,18 @@ void ViEEncoder::ReconfigureEncoder() {
|
|||||||
RTC_DCHECK(success);
|
RTC_DCHECK(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
rate_allocator_.reset(new SimulcastRateAllocator(codec));
|
video_sender_.UpdateChannelParemeters(rate_allocator_.get());
|
||||||
|
|
||||||
if (stats_proxy_) {
|
if (stats_proxy_) {
|
||||||
stats_proxy_->OnEncoderReconfigured(encoder_config_,
|
int framerate = stats_proxy_->GetSendFrameRate();
|
||||||
rate_allocator_->GetPreferedBitrate());
|
if (framerate == 0)
|
||||||
|
framerate = codec.maxFramerate;
|
||||||
|
stats_proxy_->OnEncoderReconfigured(
|
||||||
|
encoder_config_, rate_allocator_->GetPreferredBitrateBps(framerate));
|
||||||
}
|
}
|
||||||
|
|
||||||
pending_encoder_reconfiguration_ = false;
|
pending_encoder_reconfiguration_ = false;
|
||||||
if (stats_proxy_) {
|
|
||||||
stats_proxy_->OnEncoderReconfigured(encoder_config_,
|
|
||||||
rate_allocator_->GetPreferedBitrate());
|
|
||||||
}
|
|
||||||
sink_->OnEncoderConfigurationChanged(
|
sink_->OnEncoderConfigurationChanged(
|
||||||
std::move(streams), encoder_config_.min_transmit_bitrate_bps);
|
std::move(streams), encoder_config_.min_transmit_bitrate_bps);
|
||||||
}
|
}
|
||||||
@ -770,13 +636,12 @@ void ViEEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
|
|||||||
<< " rtt " << round_trip_time_ms;
|
<< " rtt " << round_trip_time_ms;
|
||||||
|
|
||||||
video_sender_.SetChannelParameters(bitrate_bps, fraction_lost,
|
video_sender_.SetChannelParameters(bitrate_bps, fraction_lost,
|
||||||
round_trip_time_ms);
|
round_trip_time_ms, rate_allocator_.get());
|
||||||
|
|
||||||
encoder_start_bitrate_bps_ =
|
encoder_start_bitrate_bps_ =
|
||||||
bitrate_bps != 0 ? bitrate_bps : encoder_start_bitrate_bps_;
|
bitrate_bps != 0 ? bitrate_bps : encoder_start_bitrate_bps_;
|
||||||
bool video_is_suspended = bitrate_bps == 0;
|
bool video_is_suspended = bitrate_bps == 0;
|
||||||
bool video_suspension_changed =
|
bool video_suspension_changed = video_is_suspended != EncoderPaused();
|
||||||
video_is_suspended != (last_observed_bitrate_bps_ == 0);
|
|
||||||
last_observed_bitrate_bps_ = bitrate_bps;
|
last_observed_bitrate_bps_ = bitrate_bps;
|
||||||
|
|
||||||
if (stats_proxy_ && video_suspension_changed) {
|
if (stats_proxy_ && video_suspension_changed) {
|
||||||
|
|||||||
@ -21,10 +21,10 @@
|
|||||||
#include "webrtc/base/task_queue.h"
|
#include "webrtc/base/task_queue.h"
|
||||||
#include "webrtc/call.h"
|
#include "webrtc/call.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
|
#include "webrtc/common_video/include/video_bitrate_allocator.h"
|
||||||
#include "webrtc/common_video/rotation.h"
|
#include "webrtc/common_video/rotation.h"
|
||||||
#include "webrtc/media/base/videosinkinterface.h"
|
#include "webrtc/media/base/videosinkinterface.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
||||||
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
|
|
||||||
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
#include "webrtc/modules/video_coding/video_coding_impl.h"
|
||||||
#include "webrtc/modules/video_processing/include/video_processing.h"
|
#include "webrtc/modules/video_processing/include/video_processing.h"
|
||||||
#include "webrtc/system_wrappers/include/atomic32.h"
|
#include "webrtc/system_wrappers/include/atomic32.h"
|
||||||
@ -184,10 +184,7 @@ class ViEEncoder : public rtc::VideoSinkInterface<VideoFrame>,
|
|||||||
rtc::ThreadChecker thread_checker_;
|
rtc::ThreadChecker thread_checker_;
|
||||||
|
|
||||||
VideoEncoderConfig encoder_config_ ACCESS_ON(&encoder_queue_);
|
VideoEncoderConfig encoder_config_ ACCESS_ON(&encoder_queue_);
|
||||||
// TODO(sprang): Change |rate_allocator_| to be a codec type
|
std::unique_ptr<VideoBitrateAllocator> rate_allocator_
|
||||||
// agnostic interface. It is currently VP8 simulcast specific if more than
|
|
||||||
// one layer is specified.
|
|
||||||
std::unique_ptr<SimulcastRateAllocator> rate_allocator_
|
|
||||||
ACCESS_ON(&encoder_queue_);
|
ACCESS_ON(&encoder_queue_);
|
||||||
|
|
||||||
// Set when ConfigureEncoder has been called in order to lazy reconfigure the
|
// Set when ConfigureEncoder has been called in order to lazy reconfigure the
|
||||||
|
|||||||
@ -93,6 +93,7 @@ class ViEEncoderTest : public ::testing::Test {
|
|||||||
|
|
||||||
VideoEncoderConfig video_encoder_config;
|
VideoEncoderConfig video_encoder_config;
|
||||||
test::FillEncoderConfiguration(1, &video_encoder_config);
|
test::FillEncoderConfiguration(1, &video_encoder_config);
|
||||||
|
video_encoder_config_ = video_encoder_config.Copy();
|
||||||
vie_encoder_.reset(new ViEEncoderUnderTest(
|
vie_encoder_.reset(new ViEEncoderUnderTest(
|
||||||
stats_proxy_.get(), video_send_config_.encoder_settings));
|
stats_proxy_.get(), video_send_config_.encoder_settings));
|
||||||
vie_encoder_->SetSink(&sink_, false /* rotation_applied */);
|
vie_encoder_->SetSink(&sink_, false /* rotation_applied */);
|
||||||
@ -236,6 +237,7 @@ class ViEEncoderTest : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
VideoSendStream::Config video_send_config_;
|
VideoSendStream::Config video_send_config_;
|
||||||
|
VideoEncoderConfig video_encoder_config_;
|
||||||
int codec_width_;
|
int codec_width_;
|
||||||
int codec_height_;
|
int codec_height_;
|
||||||
TestEncoder fake_encoder_;
|
TestEncoder fake_encoder_;
|
||||||
@ -595,6 +597,20 @@ TEST_F(ViEEncoderTest, StatsTracksAdaptationStatsWhenSwitchingSource) {
|
|||||||
vie_encoder_->Stop();
|
vie_encoder_->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ViEEncoderTest, StatsTracksPreferredBitrate) {
|
||||||
|
const int kTargetBitrateBps = 100000;
|
||||||
|
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
|
||||||
|
|
||||||
|
video_source_.IncomingCapturedFrame(CreateFrame(1, 1280, 720));
|
||||||
|
sink_.WaitForEncodedFrame(1);
|
||||||
|
|
||||||
|
VideoSendStream::Stats stats = stats_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(video_encoder_config_.max_bitrate_bps,
|
||||||
|
stats.preferred_media_bitrate_bps);
|
||||||
|
|
||||||
|
vie_encoder_->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ViEEncoderTest, UMACpuLimitedResolutionInPercent) {
|
TEST_F(ViEEncoderTest, UMACpuLimitedResolutionInPercent) {
|
||||||
const int kTargetBitrateBps = 100000;
|
const int kTargetBitrateBps = 100000;
|
||||||
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
|
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
#include "webrtc/video_frame.h"
|
#include "webrtc/video_frame.h"
|
||||||
@ -148,7 +149,17 @@ class VideoEncoder {
|
|||||||
// - framerate : The target frame rate
|
// - framerate : The target frame rate
|
||||||
//
|
//
|
||||||
// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
|
// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
|
||||||
virtual int32_t SetRates(uint32_t bitrate, uint32_t framerate) = 0;
|
virtual int32_t SetRates(uint32_t bitrate, uint32_t framerate) {
|
||||||
|
RTC_NOTREACHED() << "SetRate(uint32_t, uint32_t) is deprecated.";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default fallback: Just use the sum of bitrates as the single target rate.
|
||||||
|
// TODO(sprang): Remove this default implementation when we remove SetRates().
|
||||||
|
virtual int32_t SetRateAllocation(const BitrateAllocation& allocation,
|
||||||
|
uint32_t framerate) {
|
||||||
|
return SetRates(allocation.get_sum_kbps(), framerate);
|
||||||
|
}
|
||||||
|
|
||||||
virtual int32_t SetPeriodicKeyFrames(bool enable) { return -1; }
|
virtual int32_t SetPeriodicKeyFrames(bool enable) { return -1; }
|
||||||
virtual void OnDroppedFrame() {}
|
virtual void OnDroppedFrame() {}
|
||||||
@ -176,8 +187,8 @@ class VideoEncoderSoftwareFallbackWrapper : public VideoEncoder {
|
|||||||
const CodecSpecificInfo* codec_specific_info,
|
const CodecSpecificInfo* codec_specific_info,
|
||||||
const std::vector<FrameType>* frame_types) override;
|
const std::vector<FrameType>* frame_types) override;
|
||||||
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
|
||||||
|
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
|
||||||
int32_t SetRates(uint32_t bitrate, uint32_t framerate) override;
|
uint32_t framerate) override;
|
||||||
void OnDroppedFrame() override;
|
void OnDroppedFrame() override;
|
||||||
bool SupportsNativeHandle() const override;
|
bool SupportsNativeHandle() const override;
|
||||||
|
|
||||||
@ -192,7 +203,7 @@ class VideoEncoderSoftwareFallbackWrapper : public VideoEncoder {
|
|||||||
|
|
||||||
// The last bitrate/framerate set, and a flag for noting they are set.
|
// The last bitrate/framerate set, and a flag for noting they are set.
|
||||||
bool rates_set_;
|
bool rates_set_;
|
||||||
uint32_t bitrate_;
|
BitrateAllocation bitrate_allocation_;
|
||||||
uint32_t framerate_;
|
uint32_t framerate_;
|
||||||
|
|
||||||
// The last channel parameters set, and a flag for noting they are set.
|
// The last channel parameters set, and a flag for noting they are set.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user