webrtc_m130/media/engine/simulcast.cc
Seth Hampson 11b68463e3 Removed overloaded function GetSimulcastConfig from simulcast.cc.
GetSimulcastConfig had to be overloaded in order to not break downstream
client tests when the API was changed. Now that the downstream client
has been updated to use the new API, we can remove the overloaded
function.

Bug: webrtc:8630
Change-Id: I5d5d494e0579e60858d6efbb4715761394874b38
Reviewed-on: https://webrtc-review.googlesource.com/38882
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Seth Hampson <shampson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21590}
2018-01-11 20:15:31 +00:00

313 lines
12 KiB
C++

/*
* Copyright (c) 2014 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 <stdio.h>
#include <algorithm>
#include <string>
#include "media/base/streamparams.h"
#include "media/engine/constants.h"
#include "media/engine/simulcast.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace cricket {
struct SimulcastFormat {
int width;
int height;
// The maximum number of simulcast layers can be used for
// resolutions at |widthxheigh|.
size_t max_layers;
// The maximum bitrate for encoding stream at |widthxheight|, when we are
// not sending the next higher spatial stream.
int max_bitrate_kbps;
// The target bitrate for encoding stream at |widthxheight|, when this layer
// is not the highest layer (i.e., when we are sending another higher spatial
// stream).
int target_bitrate_kbps;
// The minimum bitrate needed for encoding stream at |widthxheight|.
int min_bitrate_kbps;
};
// These tables describe from which resolution we can use how many
// simulcast layers at what bitrates (maximum, target, and minimum).
// Important!! Keep this table from high resolution to low resolution.
const SimulcastFormat kSimulcastFormats[] = {
{1920, 1080, 3, 5000, 4000, 800},
{1280, 720, 3, 2500, 2500, 600},
{960, 540, 3, 900, 900, 450},
{640, 360, 2, 700, 500, 150},
{480, 270, 2, 450, 350, 150},
{320, 180, 1, 200, 150, 30},
{0, 0, 1, 200, 150, 30}
};
const int kMaxScreenshareSimulcastStreams = 2;
// Multiway: Number of temporal layers for each simulcast stream, for maximum
// possible number of simulcast streams |kMaxSimulcastStreams|. The array
// goes from lowest resolution at position 0 to highest resolution.
// For example, first three elements correspond to say: QVGA, VGA, WHD.
static const int
kDefaultConferenceNumberOfTemporalLayers[webrtc::kMaxSimulcastStreams] =
{3, 3, 3, 3};
void GetSimulcastSsrcs(const StreamParams& sp, std::vector<uint32_t>* ssrcs) {
const SsrcGroup* sim_group = sp.get_ssrc_group(kSimSsrcGroupSemantics);
if (sim_group) {
ssrcs->insert(
ssrcs->end(), sim_group->ssrcs.begin(), sim_group->ssrcs.end());
}
}
int FindSimulcastFormatIndex(int width, int height) {
RTC_DCHECK_GE(width, 0);
RTC_DCHECK_GE(height, 0);
for (uint32_t i = 0; i < arraysize(kSimulcastFormats); ++i) {
if (width * height >=
kSimulcastFormats[i].width * kSimulcastFormats[i].height) {
return i;
}
}
RTC_NOTREACHED();
return -1;
}
int FindSimulcastFormatIndex(int width, int height, size_t max_layers) {
RTC_DCHECK_GE(width, 0);
RTC_DCHECK_GE(height, 0);
RTC_DCHECK_GT(max_layers, 0);
for (uint32_t i = 0; i < arraysize(kSimulcastFormats); ++i) {
if (width * height >=
kSimulcastFormats[i].width * kSimulcastFormats[i].height &&
max_layers == kSimulcastFormats[i].max_layers) {
return i;
}
}
RTC_NOTREACHED();
return -1;
}
// Simulcast stream width and height must both be dividable by
// |2 ^ simulcast_layers - 1|.
int NormalizeSimulcastSize(int size, size_t simulcast_layers) {
const int base2_exponent = static_cast<int>(simulcast_layers) - 1;
return ((size >> base2_exponent) << base2_exponent);
}
size_t FindSimulcastMaxLayers(int width, int height) {
int index = FindSimulcastFormatIndex(width, height);
return kSimulcastFormats[index].max_layers;
}
int FindSimulcastMaxBitrateBps(int width, int height) {
const int format_index = FindSimulcastFormatIndex(width, height);
return kSimulcastFormats[format_index].max_bitrate_kbps * 1000;
}
int FindSimulcastTargetBitrateBps(int width, int height) {
const int format_index = FindSimulcastFormatIndex(width, height);
return kSimulcastFormats[format_index].target_bitrate_kbps * 1000;
}
int FindSimulcastMinBitrateBps(int width, int height) {
const int format_index = FindSimulcastFormatIndex(width, height);
return kSimulcastFormats[format_index].min_bitrate_kbps * 1000;
}
void SlotSimulcastMaxResolution(size_t max_layers, int* width, int* height) {
int index = FindSimulcastFormatIndex(*width, *height, max_layers);
*width = kSimulcastFormats[index].width;
*height = kSimulcastFormats[index].height;
RTC_LOG(LS_INFO) << "SlotSimulcastMaxResolution to width:" << *width
<< " height:" << *height;
}
int GetTotalMaxBitrateBps(const std::vector<webrtc::VideoStream>& streams) {
int total_max_bitrate_bps = 0;
for (size_t s = 0; s < streams.size() - 1; ++s) {
total_max_bitrate_bps += streams[s].target_bitrate_bps;
}
total_max_bitrate_bps += streams.back().max_bitrate_bps;
return total_max_bitrate_bps;
}
std::vector<webrtc::VideoStream> GetSimulcastConfig(size_t max_streams,
int width,
int height,
int max_bitrate_bps,
double bitrate_priority,
int max_qp,
int max_framerate,
bool is_screencast) {
size_t num_simulcast_layers;
if (is_screencast) {
if (UseSimulcastScreenshare()) {
num_simulcast_layers =
std::min<int>(max_streams, kMaxScreenshareSimulcastStreams);
} else {
num_simulcast_layers = 1;
}
} else {
num_simulcast_layers = FindSimulcastMaxLayers(width, height);
}
if (num_simulcast_layers > max_streams) {
// If the number of SSRCs in the group differs from our target
// number of simulcast streams for current resolution, switch down
// to a resolution that matches our number of SSRCs.
SlotSimulcastMaxResolution(max_streams, &width, &height);
num_simulcast_layers = max_streams;
}
std::vector<webrtc::VideoStream> streams;
streams.resize(num_simulcast_layers);
if (is_screencast) {
ScreenshareLayerConfig config = ScreenshareLayerConfig::GetDefault();
// For legacy screenshare in conference mode, tl0 and tl1 bitrates are
// piggybacked on the VideoCodec struct as target and max bitrates,
// respectively. See eg. webrtc::VP8EncoderImpl::SetRates().
streams[0].width = width;
streams[0].height = height;
streams[0].max_qp = max_qp;
streams[0].max_framerate = 5;
streams[0].min_bitrate_bps = kMinVideoBitrateBps;
streams[0].target_bitrate_bps = config.tl0_bitrate_kbps * 1000;
streams[0].max_bitrate_bps = config.tl1_bitrate_kbps * 1000;
streams[0].temporal_layer_thresholds_bps.clear();
streams[0].temporal_layer_thresholds_bps.push_back(config.tl0_bitrate_kbps *
1000);
// With simulcast enabled, add another spatial layer. This one will have a
// more normal layout, with the regular 3 temporal layer pattern and no fps
// restrictions. The base simulcast stream will still use legacy setup.
if (num_simulcast_layers == kMaxScreenshareSimulcastStreams) {
// Add optional upper simulcast layer.
// Lowest temporal layers of a 3 layer setup will have 40% of the total
// bitrate allocation for that stream. Make sure the gap between the
// target of the lower stream and first temporal layer of the higher one
// is at most 2x the bitrate, so that upswitching is not hampered by
// stalled bitrate estimates.
int max_bitrate_bps = 2 * ((streams[0].target_bitrate_bps * 10) / 4);
// Cap max bitrate so it isn't overly high for the given resolution.
max_bitrate_bps = std::min<int>(
max_bitrate_bps, FindSimulcastMaxBitrateBps(width, height));
streams[1].width = width;
streams[1].height = height;
streams[1].max_qp = max_qp;
streams[1].max_framerate = max_framerate;
// Three temporal layers means two thresholds.
streams[1].temporal_layer_thresholds_bps.resize(2);
streams[1].min_bitrate_bps = streams[0].target_bitrate_bps * 2;
streams[1].target_bitrate_bps = max_bitrate_bps;
streams[1].max_bitrate_bps = max_bitrate_bps;
}
} else {
// Format width and height has to be divisible by |2 ^ number_streams - 1|.
width = NormalizeSimulcastSize(width, num_simulcast_layers);
height = NormalizeSimulcastSize(height, num_simulcast_layers);
// Add simulcast sub-streams from lower resolution to higher resolutions.
// Add simulcast streams, from highest resolution (|s| = number_streams -1)
// to lowest resolution at |s| = 0.
for (size_t s = num_simulcast_layers - 1;; --s) {
streams[s].width = width;
streams[s].height = height;
// TODO(pbos): Fill actual temporal-layer bitrate thresholds.
streams[s].max_qp = max_qp;
streams[s].temporal_layer_thresholds_bps.resize(
kDefaultConferenceNumberOfTemporalLayers[s] - 1);
streams[s].max_bitrate_bps = FindSimulcastMaxBitrateBps(width, height);
streams[s].target_bitrate_bps =
FindSimulcastTargetBitrateBps(width, height);
streams[s].min_bitrate_bps = FindSimulcastMinBitrateBps(width, height);
streams[s].max_framerate = max_framerate;
width /= 2;
height /= 2;
if (s == 0)
break;
}
// Spend additional bits to boost the max stream.
int bitrate_left_bps = max_bitrate_bps - GetTotalMaxBitrateBps(streams);
if (bitrate_left_bps > 0) {
streams.back().max_bitrate_bps += bitrate_left_bps;
}
}
// The bitrate priority currently implemented on a per-sender level, so we
// just set it for the first video stream.
streams[0].bitrate_priority = bitrate_priority;
return streams;
}
static const int kScreenshareMinBitrateKbps = 50;
static const int kScreenshareMaxBitrateKbps = 6000;
static const int kScreenshareDefaultTl0BitrateKbps = 200;
static const int kScreenshareDefaultTl1BitrateKbps = 1000;
static const char* kScreencastLayerFieldTrialName =
"WebRTC-ScreenshareLayerRates";
static const char* kSimulcastScreenshareFieldTrialName =
"WebRTC-SimulcastScreenshare";
ScreenshareLayerConfig::ScreenshareLayerConfig(int tl0_bitrate, int tl1_bitrate)
: tl0_bitrate_kbps(tl0_bitrate), tl1_bitrate_kbps(tl1_bitrate) {
}
ScreenshareLayerConfig ScreenshareLayerConfig::GetDefault() {
std::string group =
webrtc::field_trial::FindFullName(kScreencastLayerFieldTrialName);
ScreenshareLayerConfig config(kScreenshareDefaultTl0BitrateKbps,
kScreenshareDefaultTl1BitrateKbps);
if (!group.empty() && !FromFieldTrialGroup(group, &config)) {
RTC_LOG(LS_WARNING) << "Unable to parse WebRTC-ScreenshareLayerRates"
" field trial group: '"
<< group << "'.";
}
return config;
}
bool ScreenshareLayerConfig::FromFieldTrialGroup(
const std::string& group,
ScreenshareLayerConfig* config) {
// Parse field trial group name, containing bitrates for tl0 and tl1.
int tl0_bitrate;
int tl1_bitrate;
if (sscanf(group.c_str(), "%d-%d", &tl0_bitrate, &tl1_bitrate) != 2) {
return false;
}
// Sanity check.
if (tl0_bitrate < kScreenshareMinBitrateKbps ||
tl0_bitrate > kScreenshareMaxBitrateKbps ||
tl1_bitrate < kScreenshareMinBitrateKbps ||
tl1_bitrate > kScreenshareMaxBitrateKbps || tl0_bitrate > tl1_bitrate) {
return false;
}
config->tl0_bitrate_kbps = tl0_bitrate;
config->tl1_bitrate_kbps = tl1_bitrate;
return true;
}
bool UseSimulcastScreenshare() {
return webrtc::field_trial::IsEnabled(kSimulcastScreenshareFieldTrialName);
}
} // namespace cricket