blob: 9721a7e9ac5b5b0058d3a7159aed678e8af4958a [file] [log] [blame]
/*
* Copyright (c) 2012 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/rtp_rtcp/source/vp8_partition_aggregator.h"
#include <assert.h>
#include <stdlib.h> // NULL
#include <algorithm>
#include <limits>
namespace webrtc {
PartitionTreeNode::PartitionTreeNode(PartitionTreeNode* parent,
const size_t* size_vector,
size_t num_partitions,
size_t this_size)
: parent_(parent),
this_size_(this_size),
size_vector_(size_vector),
num_partitions_(num_partitions),
max_parent_size_(0),
min_parent_size_(std::numeric_limits<int>::max()),
packet_start_(false) {
// If |this_size_| > INT_MAX, Cost() and CreateChildren() won't work properly.
assert(this_size_ <= static_cast<size_t>(std::numeric_limits<int>::max()));
children_[kLeftChild] = NULL;
children_[kRightChild] = NULL;
}
PartitionTreeNode* PartitionTreeNode::CreateRootNode(const size_t* size_vector,
size_t num_partitions) {
PartitionTreeNode* root_node = new PartitionTreeNode(
NULL, &size_vector[1], num_partitions - 1, size_vector[0]);
root_node->set_packet_start(true);
return root_node;
}
PartitionTreeNode::~PartitionTreeNode() {
delete children_[kLeftChild];
delete children_[kRightChild];
}
int PartitionTreeNode::Cost(size_t penalty) {
int cost = 0;
if (num_partitions_ == 0) {
// This is a solution node.
cost = std::max(max_parent_size_, this_size_int()) -
std::min(min_parent_size_, this_size_int());
} else {
cost = std::max(max_parent_size_, this_size_int()) - min_parent_size_;
}
return cost + NumPackets() * penalty;
}
bool PartitionTreeNode::CreateChildren(size_t max_size) {
assert(max_size > 0);
bool children_created = false;
if (num_partitions_ > 0) {
if (this_size_ + size_vector_[0] <= max_size) {
assert(!children_[kLeftChild]);
children_[kLeftChild] =
new PartitionTreeNode(this, &size_vector_[1], num_partitions_ - 1,
this_size_ + size_vector_[0]);
children_[kLeftChild]->set_max_parent_size(max_parent_size_);
children_[kLeftChild]->set_min_parent_size(min_parent_size_);
// "Left" child is continuation of same packet.
children_[kLeftChild]->set_packet_start(false);
children_created = true;
}
if (this_size_ > 0) {
assert(!children_[kRightChild]);
children_[kRightChild] = new PartitionTreeNode(
this, &size_vector_[1], num_partitions_ - 1, size_vector_[0]);
children_[kRightChild]->set_max_parent_size(
std::max(max_parent_size_, this_size_int()));
children_[kRightChild]->set_min_parent_size(
std::min(min_parent_size_, this_size_int()));
// "Right" child starts a new packet.
children_[kRightChild]->set_packet_start(true);
children_created = true;
}
}
return children_created;
}
size_t PartitionTreeNode::NumPackets() {
if (parent_ == NULL) {
// Root node is a "right" child by definition.
return 1;
}
if (parent_->children_[kLeftChild] == this) {
// This is a "left" child.
return parent_->NumPackets();
} else {
// This is a "right" child.
return 1 + parent_->NumPackets();
}
}
PartitionTreeNode* PartitionTreeNode::GetOptimalNode(size_t max_size,
size_t penalty) {
CreateChildren(max_size);
PartitionTreeNode* left = children_[kLeftChild];
PartitionTreeNode* right = children_[kRightChild];
if ((left == NULL) && (right == NULL)) {
// This is a solution node; return it.
return this;
} else if (left == NULL) {
// One child empty, return the other.
return right->GetOptimalNode(max_size, penalty);
} else if (right == NULL) {
// One child empty, return the other.
return left->GetOptimalNode(max_size, penalty);
} else {
PartitionTreeNode* first;
PartitionTreeNode* second;
if (left->Cost(penalty) <= right->Cost(penalty)) {
first = left;
second = right;
} else {
first = right;
second = left;
}
first = first->GetOptimalNode(max_size, penalty);
if (second->Cost(penalty) <= first->Cost(penalty)) {
second = second->GetOptimalNode(max_size, penalty);
// Compare cost estimate for "second" with actual cost for "first".
if (second->Cost(penalty) < first->Cost(penalty)) {
return second;
}
}
return first;
}
}
Vp8PartitionAggregator::Vp8PartitionAggregator(
const RTPFragmentationHeader& fragmentation,
size_t first_partition_idx,
size_t last_partition_idx)
: root_(NULL),
num_partitions_(last_partition_idx - first_partition_idx + 1),
size_vector_(new size_t[num_partitions_]),
largest_partition_size_(0) {
assert(last_partition_idx >= first_partition_idx);
assert(last_partition_idx < fragmentation.fragmentationVectorSize);
for (size_t i = 0; i < num_partitions_; ++i) {
size_vector_[i] =
fragmentation.fragmentationLength[i + first_partition_idx];
largest_partition_size_ =
std::max(largest_partition_size_, size_vector_[i]);
}
root_ = PartitionTreeNode::CreateRootNode(size_vector_, num_partitions_);
}
Vp8PartitionAggregator::~Vp8PartitionAggregator() {
delete[] size_vector_;
delete root_;
}
void Vp8PartitionAggregator::SetPriorMinMax(int min_size, int max_size) {
assert(root_);
assert(min_size >= 0);
assert(max_size >= min_size);
root_->set_min_parent_size(min_size);
root_->set_max_parent_size(max_size);
}
Vp8PartitionAggregator::ConfigVec
Vp8PartitionAggregator::FindOptimalConfiguration(size_t max_size,
size_t penalty) {
assert(root_);
assert(max_size >= largest_partition_size_);
PartitionTreeNode* opt = root_->GetOptimalNode(max_size, penalty);
ConfigVec config_vector(num_partitions_, 0);
PartitionTreeNode* temp_node = opt;
size_t packet_index = opt->NumPackets();
for (size_t i = num_partitions_; i > 0; --i) {
assert(packet_index > 0);
assert(temp_node != NULL);
config_vector[i - 1] = packet_index - 1;
if (temp_node->packet_start())
--packet_index;
temp_node = temp_node->parent();
}
return config_vector;
}
void Vp8PartitionAggregator::CalcMinMax(const ConfigVec& config,
int* min_size,
int* max_size) const {
if (*min_size < 0) {
*min_size = std::numeric_limits<int>::max();
}
if (*max_size < 0) {
*max_size = 0;
}
size_t i = 0;
while (i < config.size()) {
size_t this_size = 0;
size_t j = i;
while (j < config.size() && config[i] == config[j]) {
this_size += size_vector_[j];
++j;
}
i = j;
if (this_size < static_cast<size_t>(*min_size)) {
*min_size = this_size;
}
if (this_size > static_cast<size_t>(*max_size)) {
*max_size = this_size;
}
}
}
size_t Vp8PartitionAggregator::CalcNumberOfFragments(
size_t large_partition_size,
size_t max_payload_size,
size_t penalty,
int min_size,
int max_size) {
assert(large_partition_size > 0);
assert(max_payload_size > 0);
assert(min_size != 0);
assert(min_size <= max_size);
assert(max_size <= static_cast<int>(max_payload_size));
// Divisions with rounding up.
const size_t min_number_of_fragments =
(large_partition_size + max_payload_size - 1) / max_payload_size;
if (min_size < 0 || max_size < 0) {
// No aggregates produced, so we do not have any size boundaries.
// Simply split in as few partitions as possible.
return min_number_of_fragments;
}
const size_t max_number_of_fragments =
(large_partition_size + min_size - 1) / min_size;
int num_fragments = -1;
size_t best_cost = std::numeric_limits<size_t>::max();
for (size_t n = min_number_of_fragments; n <= max_number_of_fragments; ++n) {
// Round up so that we use the largest fragment.
size_t fragment_size = (large_partition_size + n - 1) / n;
size_t cost = 0;
if (fragment_size < static_cast<size_t>(min_size)) {
cost = min_size - fragment_size + n * penalty;
} else if (fragment_size > static_cast<size_t>(max_size)) {
cost = fragment_size - max_size + n * penalty;
} else {
cost = n * penalty;
}
if (fragment_size <= max_payload_size && cost < best_cost) {
num_fragments = n;
best_cost = cost;
}
}
assert(num_fragments > 0);
// TODO(mflodman) Assert disabled since it's falsely triggered, see issue 293.
// assert(large_partition_size / num_fragments + 1 <= max_payload_size);
return num_fragments;
}
} // namespace webrtc