OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" | 11 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" |
12 | 12 |
13 #include <assert.h> // assert | 13 #include <assert.h> // assert |
14 #include <string.h> // memcpy | 14 #include <string.h> // memcpy |
15 | 15 |
16 #include <limits> | |
17 #include <utility> | |
16 #include <vector> | 18 #include <vector> |
17 | 19 |
18 #include "webrtc/base/logging.h" | 20 #include "webrtc/base/logging.h" |
19 #include "webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h" | |
20 #include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h" | 21 #include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h" |
21 | 22 |
22 namespace webrtc { | 23 namespace webrtc { |
23 namespace { | 24 namespace { |
24 int ParseVP8PictureID(RTPVideoHeaderVP8* vp8, | 25 int ParseVP8PictureID(RTPVideoHeaderVP8* vp8, |
25 const uint8_t** data, | 26 const uint8_t** data, |
26 size_t* data_length, | 27 size_t* data_length, |
27 size_t* parsed_bytes) { | 28 size_t* parsed_bytes) { |
28 assert(vp8 != NULL); | 29 assert(vp8 != NULL); |
29 if (*data_length == 0) | 30 if (*data_length == 0) |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
133 // For an I-frame we should always have the uncompressed VP8 header | 134 // For an I-frame we should always have the uncompressed VP8 header |
134 // in the beginning of the partition. | 135 // in the beginning of the partition. |
135 return -1; | 136 return -1; |
136 } | 137 } |
137 parsed_payload->type.Video.width = ((data[7] << 8) + data[6]) & 0x3FFF; | 138 parsed_payload->type.Video.width = ((data[7] << 8) + data[6]) & 0x3FFF; |
138 parsed_payload->type.Video.height = ((data[9] << 8) + data[8]) & 0x3FFF; | 139 parsed_payload->type.Video.height = ((data[9] << 8) + data[8]) & 0x3FFF; |
139 return 0; | 140 return 0; |
140 } | 141 } |
141 } // namespace | 142 } // namespace |
142 | 143 |
143 // Define how the VP8PacketizerModes are implemented. | |
144 // Modes are: kStrict, kAggregate, kEqualSize. | |
145 const RtpPacketizerVp8::AggregationMode RtpPacketizerVp8::aggr_modes_ | |
146 [kNumModes] = {kAggrNone, kAggrPartitions, kAggrFragments}; | |
147 const bool RtpPacketizerVp8::balance_modes_[kNumModes] = {true, true, true}; | |
148 const bool RtpPacketizerVp8::separate_first_modes_[kNumModes] = {true, false, | |
149 false}; | |
150 | |
151 RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, | 144 RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, |
152 size_t max_payload_len, | 145 size_t max_payload_len, |
146 size_t last_packet_reduction_len, | |
153 VP8PacketizerMode mode) | 147 VP8PacketizerMode mode) |
154 : payload_data_(NULL), | 148 : payload_data_(NULL), |
155 payload_size_(0), | 149 payload_size_(0), |
156 vp8_fixed_payload_descriptor_bytes_(1), | 150 vp8_fixed_payload_descriptor_bytes_(1), |
157 aggr_mode_(aggr_modes_[mode]), | 151 mode_(mode), |
158 balance_(balance_modes_[mode]), | |
159 separate_first_(separate_first_modes_[mode]), | |
160 hdr_info_(hdr_info), | 152 hdr_info_(hdr_info), |
161 num_partitions_(0), | 153 num_partitions_(0), |
162 max_payload_len_(max_payload_len), | 154 max_payload_len_(max_payload_len), |
163 packets_calculated_(false) { | 155 last_packet_reduction_len_(last_packet_reduction_len) {} |
164 } | |
165 | 156 |
166 RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, | 157 RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, |
167 size_t max_payload_len) | 158 size_t max_payload_len, |
159 size_t last_packet_reduction_len) | |
168 : payload_data_(NULL), | 160 : payload_data_(NULL), |
169 payload_size_(0), | 161 payload_size_(0), |
170 part_info_(), | 162 part_info_(), |
171 vp8_fixed_payload_descriptor_bytes_(1), | 163 vp8_fixed_payload_descriptor_bytes_(1), |
172 aggr_mode_(aggr_modes_[kEqualSize]), | 164 mode_(kEqualSize), |
173 balance_(balance_modes_[kEqualSize]), | |
174 separate_first_(separate_first_modes_[kEqualSize]), | |
175 hdr_info_(hdr_info), | 165 hdr_info_(hdr_info), |
176 num_partitions_(0), | 166 num_partitions_(0), |
177 max_payload_len_(max_payload_len), | 167 max_payload_len_(max_payload_len), |
178 packets_calculated_(false) { | 168 last_packet_reduction_len_(last_packet_reduction_len) {} |
179 } | |
180 | 169 |
181 RtpPacketizerVp8::~RtpPacketizerVp8() { | 170 RtpPacketizerVp8::~RtpPacketizerVp8() { |
182 } | 171 } |
183 | 172 |
184 void RtpPacketizerVp8::SetPayloadData( | 173 size_t RtpPacketizerVp8::SetPayloadData( |
185 const uint8_t* payload_data, | 174 const uint8_t* payload_data, |
186 size_t payload_size, | 175 size_t payload_size, |
187 const RTPFragmentationHeader* fragmentation) { | 176 const RTPFragmentationHeader* fragmentation) { |
188 payload_data_ = payload_data; | 177 payload_data_ = payload_data; |
189 payload_size_ = payload_size; | 178 payload_size_ = payload_size; |
190 if (fragmentation) { | 179 if (fragmentation) { |
191 part_info_.CopyFrom(*fragmentation); | 180 part_info_.CopyFrom(*fragmentation); |
192 num_partitions_ = fragmentation->fragmentationVectorSize; | 181 num_partitions_ = fragmentation->fragmentationVectorSize; |
193 } else { | 182 } else { |
194 part_info_.VerifyAndAllocateFragmentationHeader(1); | 183 part_info_.VerifyAndAllocateFragmentationHeader(1); |
195 part_info_.fragmentationLength[0] = payload_size; | 184 part_info_.fragmentationLength[0] = payload_size; |
196 part_info_.fragmentationOffset[0] = 0; | 185 part_info_.fragmentationOffset[0] = 0; |
197 num_partitions_ = part_info_.fragmentationVectorSize; | 186 num_partitions_ = part_info_.fragmentationVectorSize; |
198 } | 187 } |
188 if (GeneratePackets() < 0) { | |
189 return 0; | |
190 } | |
191 return packets_.size(); | |
199 } | 192 } |
200 | 193 |
201 bool RtpPacketizerVp8::NextPacket(RtpPacketToSend* packet, bool* last_packet) { | 194 bool RtpPacketizerVp8::NextPacket(RtpPacketToSend* packet) { |
202 RTC_DCHECK(packet); | 195 RTC_DCHECK(packet); |
203 RTC_DCHECK(last_packet); | |
204 if (!packets_calculated_) { | |
205 int ret = 0; | |
206 if (aggr_mode_ == kAggrPartitions && balance_) { | |
207 ret = GeneratePacketsBalancedAggregates(); | |
208 } else { | |
209 ret = GeneratePackets(); | |
210 } | |
211 if (ret < 0) { | |
212 return false; | |
213 } | |
214 } | |
215 if (packets_.empty()) { | 196 if (packets_.empty()) { |
216 return false; | 197 return false; |
217 } | 198 } |
218 InfoStruct packet_info = packets_.front(); | 199 InfoStruct packet_info = packets_.front(); |
219 packets_.pop(); | 200 packets_.pop(); |
220 | 201 |
221 uint8_t* buffer = packet->AllocatePayload(max_payload_len_); | 202 uint8_t* buffer = packet->AllocatePayload( |
203 packets_.empty() ? max_payload_len_ - last_packet_reduction_len_ | |
204 : max_payload_len_); | |
222 int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); | 205 int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); |
223 if (bytes < 0) { | 206 if (bytes < 0) { |
224 return false; | 207 return false; |
225 } | 208 } |
226 packet->SetPayloadSize(bytes); | 209 packet->SetPayloadSize(bytes); |
227 *last_packet = packets_.empty(); | 210 packet->SetMarker(packets_.empty()); |
228 packet->SetMarker(*last_packet); | |
229 return true; | 211 return true; |
230 } | 212 } |
231 | 213 |
232 ProtectionType RtpPacketizerVp8::GetProtectionType() { | 214 ProtectionType RtpPacketizerVp8::GetProtectionType() { |
233 bool protect = | 215 bool protect = |
234 hdr_info_.temporalIdx == 0 || hdr_info_.temporalIdx == kNoTemporalIdx; | 216 hdr_info_.temporalIdx == 0 || hdr_info_.temporalIdx == kNoTemporalIdx; |
235 return protect ? kProtectedPacket : kUnprotectedPacket; | 217 return protect ? kProtectedPacket : kUnprotectedPacket; |
236 } | 218 } |
237 | 219 |
238 StorageType RtpPacketizerVp8::GetStorageType(uint32_t retransmission_settings) { | 220 StorageType RtpPacketizerVp8::GetStorageType(uint32_t retransmission_settings) { |
239 if (hdr_info_.temporalIdx == 0 && | 221 if (hdr_info_.temporalIdx == 0 && |
240 !(retransmission_settings & kRetransmitBaseLayer)) { | 222 !(retransmission_settings & kRetransmitBaseLayer)) { |
241 return kDontRetransmit; | 223 return kDontRetransmit; |
242 } | 224 } |
243 if (hdr_info_.temporalIdx != kNoTemporalIdx && | 225 if (hdr_info_.temporalIdx != kNoTemporalIdx && |
244 hdr_info_.temporalIdx > 0 && | 226 hdr_info_.temporalIdx > 0 && |
245 !(retransmission_settings & kRetransmitHigherLayers)) { | 227 !(retransmission_settings & kRetransmitHigherLayers)) { |
246 return kDontRetransmit; | 228 return kDontRetransmit; |
247 } | 229 } |
248 return kAllowRetransmission; | 230 return kAllowRetransmission; |
249 } | 231 } |
250 | 232 |
251 std::string RtpPacketizerVp8::ToString() { | 233 std::string RtpPacketizerVp8::ToString() { |
252 return "RtpPacketizerVp8"; | 234 return "RtpPacketizerVp8"; |
253 } | 235 } |
254 | 236 |
255 size_t RtpPacketizerVp8::CalcNextSize(size_t max_payload_len, | |
256 size_t remaining_bytes, | |
257 bool split_payload) const { | |
258 if (max_payload_len == 0 || remaining_bytes == 0) { | |
259 return 0; | |
260 } | |
261 if (!split_payload) { | |
262 return max_payload_len >= remaining_bytes ? remaining_bytes : 0; | |
263 } | |
264 | |
265 if (balance_) { | |
266 // Balance payload sizes to produce (almost) equal size | |
267 // fragments. | |
268 // Number of fragments for remaining_bytes: | |
269 size_t num_frags = remaining_bytes / max_payload_len + 1; | |
270 // Number of bytes in this fragment: | |
271 return static_cast<size_t>( | |
272 static_cast<double>(remaining_bytes) / num_frags + 0.5); | |
273 } else { | |
274 return max_payload_len >= remaining_bytes ? remaining_bytes | |
275 : max_payload_len; | |
276 } | |
277 } | |
278 | |
279 int RtpPacketizerVp8::GeneratePackets() { | 237 int RtpPacketizerVp8::GeneratePackets() { |
280 if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ + | 238 if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ + |
281 PayloadDescriptorExtraLength() + 1) { | 239 PayloadDescriptorExtraLength() + 1 + |
240 last_packet_reduction_len_) { | |
282 // The provided payload length is not long enough for the payload | 241 // The provided payload length is not long enough for the payload |
283 // descriptor and one payload byte. Return an error. | 242 // descriptor and one payload byte in the last packet. |
243 // Return an error. | |
284 return -1; | 244 return -1; |
285 } | 245 } |
286 size_t total_bytes_processed = 0; | |
287 bool start_on_new_fragment = true; | |
288 bool beginning = true; | |
289 size_t part_ix = 0; | |
290 while (total_bytes_processed < payload_size_) { | |
291 size_t packet_bytes = 0; // How much data to send in this packet. | |
292 bool split_payload = true; // Splitting of partitions is initially allowed. | |
293 size_t remaining_in_partition = part_info_.fragmentationOffset[part_ix] - | |
294 total_bytes_processed + | |
295 part_info_.fragmentationLength[part_ix]; | |
296 size_t rem_payload_len = | |
297 max_payload_len_ - | |
298 (vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength()); | |
299 size_t first_partition_in_packet = part_ix; | |
300 | 246 |
301 while (size_t next_size = CalcNextSize( | 247 size_t per_packet_capacity = |
302 rem_payload_len, remaining_in_partition, split_payload)) { | 248 max_payload_len_ - |
303 packet_bytes += next_size; | 249 (vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength()); |
304 rem_payload_len -= next_size; | |
305 remaining_in_partition -= next_size; | |
306 | 250 |
307 if (remaining_in_partition == 0 && !(beginning && separate_first_)) { | 251 if (mode_ == kEqualSize) { |
308 // Advance to next partition? | 252 GeneratePacketsSplitPayloadBalanced(0, payload_size_, per_packet_capacity, |
309 // Check that there are more partitions; verify that we are either | 253 true, 0); |
310 // allowed to aggregate fragments, or that we are allowed to | 254 return 0; |
311 // aggregate intact partitions and that we started this packet | 255 } |
312 // with an intact partition (indicated by first_fragment_ == true). | 256 size_t part_idx = 0; |
313 if (part_ix + 1 < num_partitions_ && | 257 while (part_idx < num_partitions_) { |
314 ((aggr_mode_ == kAggrFragments) || | 258 size_t current_packet_capacity = per_packet_capacity; |
315 (aggr_mode_ == kAggrPartitions && start_on_new_fragment))) { | 259 bool last_partition = (part_idx + 1) == num_partitions_; |
316 assert(part_ix < num_partitions_); | 260 if (last_partition) |
317 remaining_in_partition = part_info_.fragmentationLength[++part_ix]; | 261 current_packet_capacity -= last_packet_reduction_len_; |
318 // Disallow splitting unless kAggrFragments. In kAggrPartitions, | 262 // Check if the next partition fits in to single packet with some space |
319 // we can only aggregate intact partitions. | 263 // left to aggregate some partitions together. |
320 split_payload = (aggr_mode_ == kAggrFragments); | 264 if (mode_ == kAggregate && |
321 } | 265 part_info_.fragmentationLength[part_idx] < current_packet_capacity) { |
322 } else if (balance_ && remaining_in_partition > 0) { | 266 part_idx = |
323 break; | 267 GeneratePacketsAggregatePartitions(part_idx, per_packet_capacity); |
324 } | 268 } else { |
269 GeneratePacketsSplitPayloadBalanced( | |
270 part_info_.fragmentationOffset[part_idx], | |
271 part_info_.fragmentationLength[part_idx], per_packet_capacity, | |
272 last_partition, part_idx); | |
273 ++part_idx; | |
325 } | 274 } |
326 if (remaining_in_partition == 0) { | |
327 ++part_ix; // Advance to next partition. | |
328 } | |
329 assert(packet_bytes > 0); | |
330 | |
331 QueuePacket(total_bytes_processed, | |
332 packet_bytes, | |
333 first_partition_in_packet, | |
334 start_on_new_fragment); | |
335 total_bytes_processed += packet_bytes; | |
336 start_on_new_fragment = (remaining_in_partition == 0); | |
337 beginning = false; // Next packet cannot be first packet in frame. | |
338 } | 275 } |
339 packets_calculated_ = true; | |
340 assert(total_bytes_processed == payload_size_); | |
341 return 0; | 276 return 0; |
342 } | 277 } |
343 | 278 |
344 int RtpPacketizerVp8::GeneratePacketsBalancedAggregates() { | 279 void RtpPacketizerVp8::GeneratePacketsSplitPayloadBalanced(size_t payload_start, |
345 if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ + | 280 size_t payload_len, |
346 PayloadDescriptorExtraLength() + 1) { | 281 size_t capacity, |
347 // The provided payload length is not long enough for the payload | 282 bool last_partition, |
348 // descriptor and one payload byte. Return an error. | 283 size_t part_idx) { |
349 return -1; | 284 // Last packet of the last partition is smaller. Pretend that it's the same |
350 } | 285 // size, but we must write more payload to it. |
351 std::vector<int> partition_decision; | 286 size_t total_bytes = payload_len; |
352 const size_t overhead = | 287 if (last_partition) |
353 vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength(); | 288 total_bytes += last_packet_reduction_len_; |
354 const size_t max_payload_len = max_payload_len_ - overhead; | 289 // Integer divisions with rounding up. |
355 int min_size, max_size; | 290 size_t num_packets_left = (total_bytes + capacity - 1) / capacity; |
356 AggregateSmallPartitions(&partition_decision, &min_size, &max_size); | 291 size_t bytes_per_packet = total_bytes / num_packets_left; |
357 | 292 size_t num_larger_packets = total_bytes % num_packets_left; |
358 size_t total_bytes_processed = 0; | 293 size_t remaining_data = payload_len; |
359 size_t part_ix = 0; | 294 while (remaining_data > 0) { |
360 while (part_ix < num_partitions_) { | 295 // Last num_larger_packets are 1 byte wider than the rest. Increase |
361 if (partition_decision[part_ix] == -1) { | 296 // per-packet payload size when needed. |
362 // Split large partitions. | 297 if (num_packets_left == num_larger_packets) |
363 size_t remaining_partition = part_info_.fragmentationLength[part_ix]; | 298 ++bytes_per_packet; |
364 size_t num_fragments = Vp8PartitionAggregator::CalcNumberOfFragments( | 299 size_t current_packet_bytes = bytes_per_packet; |
365 remaining_partition, max_payload_len, overhead, min_size, max_size); | 300 if (current_packet_bytes > remaining_data) { |
366 const size_t packet_bytes = | 301 current_packet_bytes = remaining_data; |
367 (remaining_partition + num_fragments - 1) / num_fragments; | |
368 for (size_t n = 0; n < num_fragments; ++n) { | |
369 const size_t this_packet_bytes = packet_bytes < remaining_partition | |
370 ? packet_bytes | |
371 : remaining_partition; | |
372 QueuePacket( | |
373 total_bytes_processed, this_packet_bytes, part_ix, (n == 0)); | |
374 remaining_partition -= this_packet_bytes; | |
375 total_bytes_processed += this_packet_bytes; | |
376 if (static_cast<int>(this_packet_bytes) < min_size) { | |
377 min_size = this_packet_bytes; | |
378 } | |
379 if (static_cast<int>(this_packet_bytes) > max_size) { | |
380 max_size = this_packet_bytes; | |
381 } | |
382 } | |
383 assert(remaining_partition == 0); | |
384 ++part_ix; | |
385 } else { | |
386 size_t this_packet_bytes = 0; | |
387 const size_t first_partition_in_packet = part_ix; | |
388 const int aggregation_index = partition_decision[part_ix]; | |
389 while (part_ix < partition_decision.size() && | |
390 partition_decision[part_ix] == aggregation_index) { | |
391 // Collect all partitions that were aggregated into the same packet. | |
392 this_packet_bytes += part_info_.fragmentationLength[part_ix]; | |
393 ++part_ix; | |
394 } | |
395 QueuePacket(total_bytes_processed, | |
396 this_packet_bytes, | |
397 first_partition_in_packet, | |
398 true); | |
399 total_bytes_processed += this_packet_bytes; | |
400 } | 302 } |
401 } | 303 // This is not the last packet in the whole payload, but there's no data |
402 packets_calculated_ = true; | 304 // left for the last packet. Leave at least one byte for the last packet. |
403 return 0; | 305 if (num_packets_left == 2 && current_packet_bytes == remaining_data && |
404 } | 306 last_partition) { |
405 | 307 --current_packet_bytes; |
406 void RtpPacketizerVp8::AggregateSmallPartitions(std::vector<int>* partition_vec, | |
407 int* min_size, | |
408 int* max_size) { | |
409 assert(min_size && max_size); | |
410 *min_size = -1; | |
411 *max_size = -1; | |
412 assert(partition_vec); | |
413 partition_vec->assign(num_partitions_, -1); | |
414 const size_t overhead = | |
415 vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength(); | |
416 const size_t max_payload_len = max_payload_len_ - overhead; | |
417 size_t first_in_set = 0; | |
418 size_t last_in_set = 0; | |
419 int num_aggregate_packets = 0; | |
420 // Find sets of partitions smaller than max_payload_len_. | |
421 while (first_in_set < num_partitions_) { | |
422 if (part_info_.fragmentationLength[first_in_set] < max_payload_len) { | |
423 // Found start of a set. | |
424 last_in_set = first_in_set; | |
425 while (last_in_set + 1 < num_partitions_ && | |
426 part_info_.fragmentationLength[last_in_set + 1] < | |
427 max_payload_len) { | |
428 ++last_in_set; | |
429 } | |
430 // Found end of a set. Run optimized aggregator. It is ok if start == end. | |
431 Vp8PartitionAggregator aggregator(part_info_, first_in_set, last_in_set); | |
432 if (*min_size >= 0 && *max_size >= 0) { | |
433 aggregator.SetPriorMinMax(*min_size, *max_size); | |
434 } | |
435 Vp8PartitionAggregator::ConfigVec optimal_config = | |
436 aggregator.FindOptimalConfiguration(max_payload_len, overhead); | |
437 aggregator.CalcMinMax(optimal_config, min_size, max_size); | |
438 for (size_t i = first_in_set, j = 0; i <= last_in_set; ++i, ++j) { | |
439 // Transfer configuration for this set of partitions to the joint | |
440 // partition vector representing all partitions in the frame. | |
441 (*partition_vec)[i] = num_aggregate_packets + optimal_config[j]; | |
442 } | |
443 num_aggregate_packets += optimal_config.back() + 1; | |
444 first_in_set = last_in_set; | |
445 } | 308 } |
446 ++first_in_set; | 309 QueuePacket(payload_start + payload_len - remaining_data, |
310 current_packet_bytes, part_idx, remaining_data == payload_len); | |
311 remaining_data -= current_packet_bytes; | |
312 --num_packets_left; | |
447 } | 313 } |
448 } | 314 } |
449 | 315 |
316 size_t RtpPacketizerVp8::GeneratePacketsAggregatePartitions(size_t part_idx, | |
317 size_t capacity) { | |
318 // Bloat the last partition by the reduction of the last packet. As it always | |
319 // will be in the last packet we can pretend that the last packet is the same | |
320 // size as the rest of the packets. Done temporary to simplify calculations. | |
321 part_info_.fragmentationLength[num_partitions_ - 1] += | |
322 last_packet_reduction_len_; | |
323 // Current partition should fit into the packet. | |
324 RTC_CHECK_LE(part_info_.fragmentationLength[part_idx], capacity); | |
325 // Find all partitions, shorter than capacity. | |
326 size_t end_part = part_idx + 1; | |
327 while (end_part < num_partitions_ && | |
328 part_info_.fragmentationLength[end_part] <= capacity) { | |
329 end_part++; | |
330 } | |
331 size_t total_partitions = end_part - part_idx; | |
332 | |
333 // Aggregate partitions |part_idx|..|end_part|-1 to blocks of size at most | |
danilchap
2017/05/23 12:17:27
nit: put -1 inside ||: it is boundary of 'code' no
ilnik
2017/05/23 12:37:59
Done.
| |
334 // |capacity| minimizing the number of packets and then size of a largest | |
335 // block using dynamic programming. |scores[i]| stores best score in the form | |
336 // <number of packets, largest packet> for last i partitions. Maximum index is | |
337 // |total_partitions|, minimum index is 0, hence the length is | |
338 // |total_partitions|+1. | |
339 | |
340 struct PartitionScore { | |
341 size_t num_packets = std::numeric_limits<size_t>::max(); | |
342 size_t largest_packet_len = std::numeric_limits<size_t>::max(); | |
343 // Compare num_packets first then largest_packet_len | |
344 bool operator <(const PartitionScore &other) { | |
danilchap
2017/05/23 12:17:27
") const {" for symmetry with |other|
put & to the
ilnik
2017/05/23 12:37:59
Done.
| |
345 if (num_packets < other.num_packets) return true; | |
346 if (num_packets > other.num_packets) return false; | |
347 return largest_packet_len < other.largest_packet_len; | |
348 } | |
349 }; | |
350 | |
351 std::vector<PartitionScore> scores(total_partitions + 1); | |
352 // 0 partitions can be split into 0 packets with largest of size 0. | |
353 scores[0].num_packets = 0; | |
354 scores[0].largest_packet_len = 0; | |
355 | |
356 // best_block_size[i] stores optimal number of partitions to be aggregated | |
357 // in the first packet if only last i partitions are considered. | |
358 std::vector<size_t> best_block_size(total_partitions + 1, 0); | |
359 // Calculate scores and best_block_size iteratively. | |
360 for (size_t partitions_left = 0; partitions_left < total_partitions; | |
361 ++partitions_left) { | |
362 // Here scores[paritions_left] is already calculated correctly. Update | |
363 // possible score for every possible new_paritions_left > partitions_left by | |
364 // aggregating all partitions in between into a single packet. | |
365 size_t current_payload_len = 0; | |
366 PartitionScore current_score = scores[partitions_left]; | |
367 // Some next partitions are aggregated into one packet. | |
368 current_score.num_packets += 1; | |
369 // Calculate new score for last |new_partitions_left| partitions given | |
370 // best score for |partitions_left| partitions. | |
371 for (size_t new_partitions_left = partitions_left + 1; | |
372 new_partitions_left <= total_partitions; ++new_partitions_left) { | |
373 current_payload_len += | |
374 part_info_.fragmentationLength[end_part - new_partitions_left]; | |
375 if (current_payload_len > capacity) | |
376 break; | |
377 // Update maximum packet size. | |
378 if (current_payload_len > current_score.largest_packet_len) | |
379 current_score.largest_packet_len = current_payload_len; | |
380 // Score with less num_packets is better. If equal, minimum largest packet | |
381 // size is better. | |
danilchap
2017/05/23 12:17:27
I still think that using function name 'BetterThan
ilnik
2017/05/23 12:37:59
Acknowledged. This comment is a bit redundant here
| |
382 if (current_score < scores[new_partitions_left]) { | |
383 scores[new_partitions_left] = current_score; | |
384 best_block_size[new_partitions_left] = | |
385 new_partitions_left - partitions_left; | |
386 } | |
387 } | |
388 } | |
389 // Undo temporary change. | |
390 part_info_.fragmentationLength[num_partitions_ - 1] -= | |
391 last_packet_reduction_len_; | |
392 // Restore answer given sizes of aggregated blocks in |best_block_size| for | |
393 // each possible left number of partitions. | |
394 size_t partitions_left = total_partitions; | |
395 while (partitions_left > 0) { | |
396 size_t cur_parts = best_block_size[partitions_left]; | |
397 size_t first_partition = end_part - partitions_left; | |
398 size_t start_offset = part_info_.fragmentationOffset[first_partition]; | |
399 size_t post_last_partition = first_partition + cur_parts; | |
400 size_t finish_offset = | |
401 (post_last_partition < num_partitions_) | |
402 ? part_info_.fragmentationOffset[post_last_partition] | |
403 : payload_size_; | |
404 size_t current_payload_len = finish_offset - start_offset; | |
405 QueuePacket(start_offset, current_payload_len, first_partition, true); | |
406 // Go to next packet. | |
407 partitions_left -= cur_parts; | |
408 } | |
409 return end_part; | |
410 } | |
411 | |
450 void RtpPacketizerVp8::QueuePacket(size_t start_pos, | 412 void RtpPacketizerVp8::QueuePacket(size_t start_pos, |
451 size_t packet_size, | 413 size_t packet_size, |
452 size_t first_partition_in_packet, | 414 size_t first_partition_in_packet, |
453 bool start_on_new_fragment) { | 415 bool start_on_new_fragment) { |
454 // Write info to packet info struct and store in packet info queue. | 416 // Write info to packet info struct and store in packet info queue. |
455 InfoStruct packet_info; | 417 InfoStruct packet_info; |
456 packet_info.payload_start_pos = start_pos; | 418 packet_info.payload_start_pos = start_pos; |
457 packet_info.size = packet_size; | 419 packet_info.size = packet_size; |
458 packet_info.first_partition_ix = first_partition_in_packet; | 420 packet_info.first_partition_ix = first_partition_in_packet; |
459 packet_info.first_fragment = start_on_new_fragment; | 421 packet_info.first_fragment = start_on_new_fragment; |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
736 if (ParseVP8FrameSize(parsed_payload, payload_data, payload_data_length) != | 698 if (ParseVP8FrameSize(parsed_payload, payload_data, payload_data_length) != |
737 0) { | 699 0) { |
738 return false; | 700 return false; |
739 } | 701 } |
740 | 702 |
741 parsed_payload->payload = payload_data; | 703 parsed_payload->payload = payload_data; |
742 parsed_payload->payload_length = payload_data_length; | 704 parsed_payload->payload_length = payload_data_length; |
743 return true; | 705 return true; |
744 } | 706 } |
745 } // namespace webrtc | 707 } // namespace webrtc |
OLD | NEW |