1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h" 12 13 #include <assert.h> 14 #include <stdlib.h> // NULL 15 16 #include <algorithm> 17 #include <limits> 18 19 namespace webrtc { 20 21 PartitionTreeNode::PartitionTreeNode(PartitionTreeNode* parent, 22 const int* size_vector, 23 int num_partitions, 24 int this_size) 25 : parent_(parent), 26 this_size_(this_size), 27 size_vector_(size_vector), 28 num_partitions_(num_partitions), 29 max_parent_size_(0), 30 min_parent_size_(std::numeric_limits<int>::max()), 31 packet_start_(false) { 32 assert(num_partitions >= 0); 33 children_[kLeftChild] = NULL; 34 children_[kRightChild] = NULL; 35 } 36 37 PartitionTreeNode* PartitionTreeNode::CreateRootNode(const int* size_vector, 38 int num_partitions) { 39 PartitionTreeNode* root_node = 40 new PartitionTreeNode(NULL, &size_vector[1], num_partitions - 1, 41 size_vector[0]); 42 root_node->set_packet_start(true); 43 return root_node; 44 } 45 46 PartitionTreeNode::~PartitionTreeNode() { 47 delete children_[kLeftChild]; 48 delete children_[kRightChild]; 49 } 50 51 int PartitionTreeNode::Cost(int penalty) { 52 assert(penalty >= 0); 53 int cost = 0; 54 if (num_partitions_ == 0) { 55 // This is a solution node. 56 cost = std::max(max_parent_size_, this_size_) - 57 std::min(min_parent_size_, this_size_); 58 } else { 59 cost = std::max(max_parent_size_, this_size_) - min_parent_size_; 60 } 61 return cost + NumPackets() * penalty; 62 } 63 64 bool PartitionTreeNode::CreateChildren(int max_size) { 65 assert(max_size > 0); 66 bool children_created = false; 67 if (num_partitions_ > 0) { 68 if (this_size_ + size_vector_[0] <= max_size) { 69 assert(!children_[kLeftChild]); 70 children_[kLeftChild] = 71 new PartitionTreeNode(this, 72 &size_vector_[1], 73 num_partitions_ - 1, 74 this_size_ + size_vector_[0]); 75 children_[kLeftChild]->set_max_parent_size(max_parent_size_); 76 children_[kLeftChild]->set_min_parent_size(min_parent_size_); 77 // "Left" child is continuation of same packet. 78 children_[kLeftChild]->set_packet_start(false); 79 children_created = true; 80 } 81 if (this_size_ > 0) { 82 assert(!children_[kRightChild]); 83 children_[kRightChild] = new PartitionTreeNode(this, 84 &size_vector_[1], 85 num_partitions_ - 1, 86 size_vector_[0]); 87 children_[kRightChild]->set_max_parent_size( 88 std::max(max_parent_size_, this_size_)); 89 children_[kRightChild]->set_min_parent_size( 90 std::min(min_parent_size_, this_size_)); 91 // "Right" child starts a new packet. 92 children_[kRightChild]->set_packet_start(true); 93 children_created = true; 94 } 95 } 96 return children_created; 97 } 98 99 int PartitionTreeNode::NumPackets() { 100 if (parent_ == NULL) { 101 // Root node is a "right" child by definition. 102 return 1; 103 } 104 if (parent_->children_[kLeftChild] == this) { 105 // This is a "left" child. 106 return parent_->NumPackets(); 107 } else { 108 // This is a "right" child. 109 return 1 + parent_->NumPackets(); 110 } 111 } 112 113 PartitionTreeNode* PartitionTreeNode::GetOptimalNode(int max_size, 114 int penalty) { 115 CreateChildren(max_size); 116 PartitionTreeNode* left = children_[kLeftChild]; 117 PartitionTreeNode* right = children_[kRightChild]; 118 if ((left == NULL) && (right == NULL)) { 119 // This is a solution node; return it. 120 return this; 121 } else if (left == NULL) { 122 // One child empty, return the other. 123 return right->GetOptimalNode(max_size, penalty); 124 } else if (right == NULL) { 125 // One child empty, return the other. 126 return left->GetOptimalNode(max_size, penalty); 127 } else { 128 PartitionTreeNode* first; 129 PartitionTreeNode* second; 130 if (left->Cost(penalty) <= right->Cost(penalty)) { 131 first = left; 132 second = right; 133 } else { 134 first = right; 135 second = left; 136 } 137 first = first->GetOptimalNode(max_size, penalty); 138 if (second->Cost(penalty) <= first->Cost(penalty)) { 139 second = second->GetOptimalNode(max_size, penalty); 140 // Compare cost estimate for "second" with actual cost for "first". 141 if (second->Cost(penalty) < first->Cost(penalty)) { 142 return second; 143 } 144 } 145 return first; 146 } 147 } 148 149 Vp8PartitionAggregator::Vp8PartitionAggregator( 150 const RTPFragmentationHeader& fragmentation, 151 int first_partition_idx, int last_partition_idx) 152 : root_(NULL), 153 num_partitions_(last_partition_idx - first_partition_idx + 1), 154 size_vector_(new int[num_partitions_]), 155 largest_partition_size_(0) { 156 assert(first_partition_idx >= 0); 157 assert(last_partition_idx >= first_partition_idx); 158 assert(last_partition_idx < fragmentation.fragmentationVectorSize); 159 for (size_t i = 0; i < num_partitions_; ++i) { 160 size_vector_[i] = 161 fragmentation.fragmentationLength[i + first_partition_idx]; 162 largest_partition_size_ = std::max(largest_partition_size_, 163 size_vector_[i]); 164 } 165 root_ = PartitionTreeNode::CreateRootNode(size_vector_, num_partitions_); 166 } 167 168 Vp8PartitionAggregator::~Vp8PartitionAggregator() { 169 delete [] size_vector_; 170 delete root_; 171 } 172 173 void Vp8PartitionAggregator::SetPriorMinMax(int min_size, int max_size) { 174 assert(root_); 175 assert(min_size >= 0); 176 assert(max_size >= min_size); 177 root_->set_min_parent_size(min_size); 178 root_->set_max_parent_size(max_size); 179 } 180 181 Vp8PartitionAggregator::ConfigVec 182 Vp8PartitionAggregator::FindOptimalConfiguration(int max_size, int penalty) { 183 assert(root_); 184 assert(max_size >= largest_partition_size_); 185 PartitionTreeNode* opt = root_->GetOptimalNode(max_size, penalty); 186 ConfigVec config_vector(num_partitions_, 0); 187 PartitionTreeNode* temp_node = opt; 188 int packet_index = opt->NumPackets() - 1; 189 for (int i = num_partitions_ - 1; i >= 0; --i) { 190 assert(packet_index >= 0); 191 assert(temp_node != NULL); 192 config_vector[i] = packet_index; 193 if (temp_node->packet_start()) --packet_index; 194 temp_node = temp_node->parent(); 195 } 196 return config_vector; 197 } 198 199 void Vp8PartitionAggregator::CalcMinMax(const ConfigVec& config, 200 int* min_size, int* max_size) const { 201 if (*min_size < 0) { 202 *min_size = std::numeric_limits<int>::max(); 203 } 204 if (*max_size < 0) { 205 *max_size = 0; 206 } 207 unsigned int i = 0; 208 while (i < config.size()) { 209 int this_size = 0; 210 unsigned int j = i; 211 while (j < config.size() && config[i] == config[j]) { 212 this_size += size_vector_[j]; 213 ++j; 214 } 215 i = j; 216 if (this_size < *min_size) { 217 *min_size = this_size; 218 } 219 if (this_size > *max_size) { 220 *max_size = this_size; 221 } 222 } 223 } 224 225 int Vp8PartitionAggregator::CalcNumberOfFragments(int large_partition_size, 226 int max_payload_size, 227 int penalty, 228 int min_size, 229 int max_size) { 230 assert(max_size <= max_payload_size); 231 assert(min_size <= max_size); 232 assert(max_payload_size > 0); 233 // Divisions with rounding up. 234 const int min_number_of_fragments = 235 (large_partition_size + max_payload_size - 1) / max_payload_size; 236 if (min_size < 0 || max_size < 0) { 237 // No aggregates produced, so we do not have any size boundaries. 238 // Simply split in as few partitions as possible. 239 return min_number_of_fragments; 240 } 241 const int max_number_of_fragments = 242 (large_partition_size + min_size - 1) / min_size; 243 int num_fragments = -1; 244 int best_cost = std::numeric_limits<int>::max(); 245 for (int n = min_number_of_fragments; n <= max_number_of_fragments; ++n) { 246 // Round up so that we use the largest fragment. 247 int fragment_size = (large_partition_size + n - 1) / n; 248 int cost = 0; 249 if (fragment_size < min_size) { 250 cost = min_size - fragment_size + n * penalty; 251 } else if (fragment_size > max_size) { 252 cost = fragment_size - max_size + n * penalty; 253 } else { 254 cost = n * penalty; 255 } 256 if (fragment_size <= max_payload_size && cost < best_cost) { 257 num_fragments = n; 258 best_cost = cost; 259 } 260 } 261 assert(num_fragments > 0); 262 // TODO(mflodman) Assert disabled since it's falsely triggered, see issue 293. 263 //assert(large_partition_size / num_fragments + 1 <= max_payload_size); 264 return num_fragments; 265 } 266 267 } // namespace 268