Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(363)

Unified Diff: webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc

Issue 2871173008: Fix packetization logic to leave space for extensions in the last packet (Closed)
Patch Set: Impelement Danilchap@ comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc
index a038d47179d441b877974ad32115724df7871bae..1139a8d34a2c793ebb8c757b6416a36209aaeb2b 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc
@@ -13,10 +13,11 @@
#include <assert.h> // assert
#include <string.h> // memcpy
+#include <limits>
+#include <utility>
#include <vector>
#include "webrtc/base/logging.h"
-#include "webrtc/modules/rtp_rtcp/source/vp8_partition_aggregator.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h"
namespace webrtc {
@@ -140,48 +141,36 @@ int ParseVP8FrameSize(RtpDepacketizer::ParsedPayload* parsed_payload,
}
} // namespace
-// Define how the VP8PacketizerModes are implemented.
-// Modes are: kStrict, kAggregate, kEqualSize.
-const RtpPacketizerVp8::AggregationMode RtpPacketizerVp8::aggr_modes_
- [kNumModes] = {kAggrNone, kAggrPartitions, kAggrFragments};
-const bool RtpPacketizerVp8::balance_modes_[kNumModes] = {true, true, true};
-const bool RtpPacketizerVp8::separate_first_modes_[kNumModes] = {true, false,
- false};
-
RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info,
size_t max_payload_len,
+ size_t last_packet_reduction_len,
VP8PacketizerMode mode)
: payload_data_(NULL),
payload_size_(0),
vp8_fixed_payload_descriptor_bytes_(1),
- aggr_mode_(aggr_modes_[mode]),
- balance_(balance_modes_[mode]),
- separate_first_(separate_first_modes_[mode]),
+ mode_(mode),
hdr_info_(hdr_info),
num_partitions_(0),
max_payload_len_(max_payload_len),
- packets_calculated_(false) {
-}
+ last_packet_reduction_len_(last_packet_reduction_len) {}
RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info,
- size_t max_payload_len)
+ size_t max_payload_len,
+ size_t last_packet_reduction_len)
: payload_data_(NULL),
payload_size_(0),
part_info_(),
vp8_fixed_payload_descriptor_bytes_(1),
- aggr_mode_(aggr_modes_[kEqualSize]),
- balance_(balance_modes_[kEqualSize]),
- separate_first_(separate_first_modes_[kEqualSize]),
+ mode_(kEqualSize),
hdr_info_(hdr_info),
num_partitions_(0),
max_payload_len_(max_payload_len),
- packets_calculated_(false) {
-}
+ last_packet_reduction_len_(last_packet_reduction_len) {}
RtpPacketizerVp8::~RtpPacketizerVp8() {
}
-void RtpPacketizerVp8::SetPayloadData(
+size_t RtpPacketizerVp8::SetPayloadData(
const uint8_t* payload_data,
size_t payload_size,
const RTPFragmentationHeader* fragmentation) {
@@ -196,36 +185,29 @@ void RtpPacketizerVp8::SetPayloadData(
part_info_.fragmentationOffset[0] = 0;
num_partitions_ = part_info_.fragmentationVectorSize;
}
+ if (GeneratePackets() < 0) {
+ return 0;
+ }
+ return packets_.size();
}
-bool RtpPacketizerVp8::NextPacket(RtpPacketToSend* packet, bool* last_packet) {
+bool RtpPacketizerVp8::NextPacket(RtpPacketToSend* packet) {
RTC_DCHECK(packet);
- RTC_DCHECK(last_packet);
- if (!packets_calculated_) {
- int ret = 0;
- if (aggr_mode_ == kAggrPartitions && balance_) {
- ret = GeneratePacketsBalancedAggregates();
- } else {
- ret = GeneratePackets();
- }
- if (ret < 0) {
- return false;
- }
- }
if (packets_.empty()) {
return false;
}
InfoStruct packet_info = packets_.front();
packets_.pop();
- uint8_t* buffer = packet->AllocatePayload(max_payload_len_);
+ uint8_t* buffer = packet->AllocatePayload(
+ packets_.empty() ? max_payload_len_ - last_packet_reduction_len_
+ : max_payload_len_);
int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_);
if (bytes < 0) {
return false;
}
packet->SetPayloadSize(bytes);
- *last_packet = packets_.empty();
- packet->SetMarker(*last_packet);
+ packet->SetMarker(packets_.empty());
return true;
}
@@ -252,199 +234,179 @@ std::string RtpPacketizerVp8::ToString() {
return "RtpPacketizerVp8";
}
-size_t RtpPacketizerVp8::CalcNextSize(size_t max_payload_len,
- size_t remaining_bytes,
- bool split_payload) const {
- if (max_payload_len == 0 || remaining_bytes == 0) {
- return 0;
- }
- if (!split_payload) {
- return max_payload_len >= remaining_bytes ? remaining_bytes : 0;
- }
-
- if (balance_) {
- // Balance payload sizes to produce (almost) equal size
- // fragments.
- // Number of fragments for remaining_bytes:
- size_t num_frags = remaining_bytes / max_payload_len + 1;
- // Number of bytes in this fragment:
- return static_cast<size_t>(
- static_cast<double>(remaining_bytes) / num_frags + 0.5);
- } else {
- return max_payload_len >= remaining_bytes ? remaining_bytes
- : max_payload_len;
- }
-}
-
int RtpPacketizerVp8::GeneratePackets() {
if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ +
- PayloadDescriptorExtraLength() + 1) {
+ PayloadDescriptorExtraLength() + 1 +
+ last_packet_reduction_len_) {
// The provided payload length is not long enough for the payload
- // descriptor and one payload byte. Return an error.
+ // descriptor and one payload byte in the last packet.
+ // Return an error.
return -1;
}
- size_t total_bytes_processed = 0;
- bool start_on_new_fragment = true;
- bool beginning = true;
- size_t part_ix = 0;
- while (total_bytes_processed < payload_size_) {
- size_t packet_bytes = 0; // How much data to send in this packet.
- bool split_payload = true; // Splitting of partitions is initially allowed.
- size_t remaining_in_partition = part_info_.fragmentationOffset[part_ix] -
- total_bytes_processed +
- part_info_.fragmentationLength[part_ix];
- size_t rem_payload_len =
- max_payload_len_ -
- (vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength());
- size_t first_partition_in_packet = part_ix;
-
- while (size_t next_size = CalcNextSize(
- rem_payload_len, remaining_in_partition, split_payload)) {
- packet_bytes += next_size;
- rem_payload_len -= next_size;
- remaining_in_partition -= next_size;
-
- if (remaining_in_partition == 0 && !(beginning && separate_first_)) {
- // Advance to next partition?
- // Check that there are more partitions; verify that we are either
- // allowed to aggregate fragments, or that we are allowed to
- // aggregate intact partitions and that we started this packet
- // with an intact partition (indicated by first_fragment_ == true).
- if (part_ix + 1 < num_partitions_ &&
- ((aggr_mode_ == kAggrFragments) ||
- (aggr_mode_ == kAggrPartitions && start_on_new_fragment))) {
- assert(part_ix < num_partitions_);
- remaining_in_partition = part_info_.fragmentationLength[++part_ix];
- // Disallow splitting unless kAggrFragments. In kAggrPartitions,
- // we can only aggregate intact partitions.
- split_payload = (aggr_mode_ == kAggrFragments);
- }
- } else if (balance_ && remaining_in_partition > 0) {
- break;
- }
- }
- if (remaining_in_partition == 0) {
- ++part_ix; // Advance to next partition.
- }
- assert(packet_bytes > 0);
-
- QueuePacket(total_bytes_processed,
- packet_bytes,
- first_partition_in_packet,
- start_on_new_fragment);
- total_bytes_processed += packet_bytes;
- start_on_new_fragment = (remaining_in_partition == 0);
- beginning = false; // Next packet cannot be first packet in frame.
- }
- packets_calculated_ = true;
- assert(total_bytes_processed == payload_size_);
- return 0;
-}
-int RtpPacketizerVp8::GeneratePacketsBalancedAggregates() {
- if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ +
- PayloadDescriptorExtraLength() + 1) {
- // The provided payload length is not long enough for the payload
- // descriptor and one payload byte. Return an error.
- return -1;
+ size_t per_packet_capacity =
+ max_payload_len_ -
+ (vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength());
+
+ if (mode_ == kEqualSize) {
+ GeneratePacketsSplitPayloadBalanced(0, payload_size_, per_packet_capacity,
+ true, 0);
+ return 0;
}
- std::vector<int> partition_decision;
- const size_t overhead =
- vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength();
- const size_t max_payload_len = max_payload_len_ - overhead;
- int min_size, max_size;
- AggregateSmallPartitions(&partition_decision, &min_size, &max_size);
-
- size_t total_bytes_processed = 0;
- size_t part_ix = 0;
- while (part_ix < num_partitions_) {
- if (partition_decision[part_ix] == -1) {
- // Split large partitions.
- size_t remaining_partition = part_info_.fragmentationLength[part_ix];
- size_t num_fragments = Vp8PartitionAggregator::CalcNumberOfFragments(
- remaining_partition, max_payload_len, overhead, min_size, max_size);
- const size_t packet_bytes =
- (remaining_partition + num_fragments - 1) / num_fragments;
- for (size_t n = 0; n < num_fragments; ++n) {
- const size_t this_packet_bytes = packet_bytes < remaining_partition
- ? packet_bytes
- : remaining_partition;
- QueuePacket(
- total_bytes_processed, this_packet_bytes, part_ix, (n == 0));
- remaining_partition -= this_packet_bytes;
- total_bytes_processed += this_packet_bytes;
- if (static_cast<int>(this_packet_bytes) < min_size) {
- min_size = this_packet_bytes;
- }
- if (static_cast<int>(this_packet_bytes) > max_size) {
- max_size = this_packet_bytes;
- }
- }
- assert(remaining_partition == 0);
- ++part_ix;
+ size_t part_idx = 0;
+ while (part_idx < num_partitions_) {
+ size_t current_packet_capacity = per_packet_capacity;
+ bool last_partition = (part_idx + 1) == num_partitions_;
+ if (last_partition)
+ current_packet_capacity -= last_packet_reduction_len_;
+ // Check if the next partition fits in to single packet with some space
+ // left to aggregate some partitions together.
+ if (mode_ == kAggregate &&
+ part_info_.fragmentationLength[part_idx] < current_packet_capacity) {
+ part_idx =
+ GeneratePacketsAggregatePartitions(part_idx, per_packet_capacity);
} else {
- size_t this_packet_bytes = 0;
- const size_t first_partition_in_packet = part_ix;
- const int aggregation_index = partition_decision[part_ix];
- while (part_ix < partition_decision.size() &&
- partition_decision[part_ix] == aggregation_index) {
- // Collect all partitions that were aggregated into the same packet.
- this_packet_bytes += part_info_.fragmentationLength[part_ix];
- ++part_ix;
- }
- QueuePacket(total_bytes_processed,
- this_packet_bytes,
- first_partition_in_packet,
- true);
- total_bytes_processed += this_packet_bytes;
+ GeneratePacketsSplitPayloadBalanced(
+ part_info_.fragmentationOffset[part_idx],
+ part_info_.fragmentationLength[part_idx], per_packet_capacity,
+ last_partition, part_idx);
+ ++part_idx;
}
}
- packets_calculated_ = true;
return 0;
}
-void RtpPacketizerVp8::AggregateSmallPartitions(std::vector<int>* partition_vec,
- int* min_size,
- int* max_size) {
- assert(min_size && max_size);
- *min_size = -1;
- *max_size = -1;
- assert(partition_vec);
- partition_vec->assign(num_partitions_, -1);
- const size_t overhead =
- vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength();
- const size_t max_payload_len = max_payload_len_ - overhead;
- size_t first_in_set = 0;
- size_t last_in_set = 0;
- int num_aggregate_packets = 0;
- // Find sets of partitions smaller than max_payload_len_.
- while (first_in_set < num_partitions_) {
- if (part_info_.fragmentationLength[first_in_set] < max_payload_len) {
- // Found start of a set.
- last_in_set = first_in_set;
- while (last_in_set + 1 < num_partitions_ &&
- part_info_.fragmentationLength[last_in_set + 1] <
- max_payload_len) {
- ++last_in_set;
- }
- // Found end of a set. Run optimized aggregator. It is ok if start == end.
- Vp8PartitionAggregator aggregator(part_info_, first_in_set, last_in_set);
- if (*min_size >= 0 && *max_size >= 0) {
- aggregator.SetPriorMinMax(*min_size, *max_size);
- }
- Vp8PartitionAggregator::ConfigVec optimal_config =
- aggregator.FindOptimalConfiguration(max_payload_len, overhead);
- aggregator.CalcMinMax(optimal_config, min_size, max_size);
- for (size_t i = first_in_set, j = 0; i <= last_in_set; ++i, ++j) {
- // Transfer configuration for this set of partitions to the joint
- // partition vector representing all partitions in the frame.
- (*partition_vec)[i] = num_aggregate_packets + optimal_config[j];
+void RtpPacketizerVp8::GeneratePacketsSplitPayloadBalanced(size_t payload_start,
+ size_t payload_len,
+ size_t capacity,
+ bool last_partition,
+ size_t part_idx) {
+ // Last packet of the last partition is smaller. Pretend that it's the same
+ // size, but we must write more payload to it.
+ size_t total_bytes = payload_len;
+ if (last_partition)
+ total_bytes += last_packet_reduction_len_;
+ // Integer divisions with rounding up.
+ size_t num_packets_left = (total_bytes + capacity - 1) / capacity;
+ size_t bytes_per_packet = total_bytes / num_packets_left;
+ size_t num_larger_packets = total_bytes % num_packets_left;
+ size_t remaining_data = payload_len;
+ while (remaining_data > 0) {
+ // Last num_larger_packets are 1 byte wider than the rest. Increase
+ // per-packet payload size when needed.
+ if (num_packets_left == num_larger_packets)
+ ++bytes_per_packet;
+ size_t current_packet_bytes = bytes_per_packet;
+ if (current_packet_bytes > remaining_data) {
+ current_packet_bytes = remaining_data;
+ }
+ // This is not the last packet in the whole payload, but there's no data
+ // left for the last packet. Leave at least one byte for the last packet.
+ if (num_packets_left == 2 && current_packet_bytes == remaining_data &&
+ last_partition) {
+ --current_packet_bytes;
+ }
+ QueuePacket(payload_start + payload_len - remaining_data,
+ current_packet_bytes, part_idx, remaining_data == payload_len);
+ remaining_data -= current_packet_bytes;
+ --num_packets_left;
+ }
+}
+
+size_t RtpPacketizerVp8::GeneratePacketsAggregatePartitions(size_t part_idx,
+ size_t capacity) {
+ // Bloat the last partition by the reduction of the last packet. As it always
+ // will be in the last packet we can pretend that the last packet is the same
+ // size as the rest of the packets. Done temporary to simplify calculations.
+ part_info_.fragmentationLength[num_partitions_ - 1] +=
+ last_packet_reduction_len_;
+ // Current partition should fit into the packet.
+ RTC_CHECK_LE(part_info_.fragmentationLength[part_idx], capacity);
+ // Find all partitions, shorter than capacity.
+ size_t end_part = part_idx + 1;
+ while (end_part < num_partitions_ &&
+ part_info_.fragmentationLength[end_part] <= capacity) {
+ ++end_part;
+ }
+ size_t total_partitions = end_part - part_idx;
+
+ // Aggregate partitions |part_idx|..|end_part-1| to blocks of size at most
+ // |capacity| minimizing the number of packets and then size of a largest
+ // block using dynamic programming. |scores[i]| stores best score in the form
+ // <number of packets, largest packet> for last i partitions. Maximum index is
+ // |total_partitions|, minimum index is 0, hence the length is
+ // |total_partitions|+1.
+
+ struct PartitionScore {
+ size_t num_packets = std::numeric_limits<size_t>::max();
+ size_t largest_packet_len = std::numeric_limits<size_t>::max();
+ // Compare num_packets first then largest_packet_len
+ bool operator <(const PartitionScore& other) const {
+ if (num_packets < other.num_packets) return true;
+ if (num_packets > other.num_packets) return false;
+ return largest_packet_len < other.largest_packet_len;
+ }
+ };
+
+ std::vector<PartitionScore> scores(total_partitions + 1);
+ // 0 partitions can be split into 0 packets with largest of size 0.
+ scores[0].num_packets = 0;
+ scores[0].largest_packet_len = 0;
+
+ // best_block_size[i] stores optimal number of partitions to be aggregated
+ // in the first packet if only last i partitions are considered.
+ std::vector<size_t> best_block_size(total_partitions + 1, 0);
+ // Calculate scores and best_block_size iteratively.
+ for (size_t partitions_left = 0; partitions_left < total_partitions;
+ ++partitions_left) {
+ // Here scores[paritions_left] is already calculated correctly. Update
+ // possible score for every possible new_paritions_left > partitions_left by
+ // aggregating all partitions in between into a single packet.
+ size_t current_payload_len = 0;
+ PartitionScore current_score = scores[partitions_left];
+ // Some next partitions are aggregated into one packet.
+ current_score.num_packets += 1;
+ // Calculate new score for last |new_partitions_left| partitions given
+ // best score for |partitions_left| partitions.
+ for (size_t new_partitions_left = partitions_left + 1;
+ new_partitions_left <= total_partitions; ++new_partitions_left) {
+ current_payload_len +=
+ part_info_.fragmentationLength[end_part - new_partitions_left];
+ if (current_payload_len > capacity)
+ break;
+ // Update maximum packet size.
+ if (current_payload_len > current_score.largest_packet_len)
+ current_score.largest_packet_len = current_payload_len;
+ // Score with less num_packets is better. If equal, minimum largest packet
+ // size is better.
+ if (current_score < scores[new_partitions_left]) {
+ scores[new_partitions_left] = current_score;
+ best_block_size[new_partitions_left] =
+ new_partitions_left - partitions_left;
}
- num_aggregate_packets += optimal_config.back() + 1;
- first_in_set = last_in_set;
}
- ++first_in_set;
}
+ // Undo temporary change.
+ part_info_.fragmentationLength[num_partitions_ - 1] -=
+ last_packet_reduction_len_;
+ // Restore answer given sizes of aggregated blocks in |best_block_size| for
+ // each possible left number of partitions.
+ size_t partitions_left = total_partitions;
+ while (partitions_left > 0) {
+ size_t cur_parts = best_block_size[partitions_left];
+ size_t first_partition = end_part - partitions_left;
+ size_t start_offset = part_info_.fragmentationOffset[first_partition];
+ size_t post_last_partition = first_partition + cur_parts;
+ size_t finish_offset =
+ (post_last_partition < num_partitions_)
+ ? part_info_.fragmentationOffset[post_last_partition]
+ : payload_size_;
+ size_t current_payload_len = finish_offset - start_offset;
+ QueuePacket(start_offset, current_payload_len, first_partition, true);
+ // Go to next packet.
+ partitions_left -= cur_parts;
+ }
+ return end_part;
}
void RtpPacketizerVp8::QueuePacket(size_t start_pos,
« no previous file with comments | « webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h ('k') | webrtc/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698