Index: webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc |
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc |
index e190ea2f6aeea5f8e261cec86c6782477e867d32..11b13a67f93cdefc1fdfbae6bcf68411073a079c 100644 |
--- a/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc |
+++ b/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc |
@@ -79,8 +79,11 @@ bool ParseStapAStartOffsets(const uint8_t* nalu_ptr, |
} // namespace |
RtpPacketizerH264::RtpPacketizerH264(size_t max_payload_len, |
+ size_t last_packet_reduction_len, |
H264PacketizationMode packetization_mode) |
: max_payload_len_(max_payload_len), |
+ last_packet_reduction_len_(last_packet_reduction_len), |
+ total_packets_(0), |
packetization_mode_(packetization_mode) { |
// Guard against uninitialized memory in packetization_mode. |
RTC_CHECK(packetization_mode == H264PacketizationMode::NonInterleaved || |
@@ -95,7 +98,7 @@ RtpPacketizerH264::Fragment::Fragment(const uint8_t* buffer, size_t length) |
RtpPacketizerH264::Fragment::Fragment(const Fragment& fragment) |
: buffer(fragment.buffer), length(fragment.length) {} |
-void RtpPacketizerH264::SetPayloadData( |
+size_t RtpPacketizerH264::SetPayloadData( |
const uint8_t* payload_data, |
size_t payload_size, |
const RTPFragmentationHeader* fragmentation) { |
@@ -164,6 +167,7 @@ void RtpPacketizerH264::SetPayloadData( |
input_fragments_.push_back(Fragment(buffer, length)); |
} |
GeneratePackets(); |
+ return total_packets_; |
} |
void RtpPacketizerH264::GeneratePackets() { |
@@ -174,7 +178,10 @@ void RtpPacketizerH264::GeneratePackets() { |
++i; |
break; |
case H264PacketizationMode::NonInterleaved: |
- if (input_fragments_[i].length > max_payload_len_) { |
+ if (input_fragments_[i].length > max_payload_len_ || |
+ (i + 1 == input_fragments_.size() && |
+ (input_fragments_[i].length + last_packet_reduction_len_ > |
+ max_payload_len_))) { |
sprang_webrtc
2017/05/17 13:10:07
nit: Maybe you can clarify this a bit by having a
ilnik
2017/05/17 15:06:33
Done.
|
PacketizeFuA(i); |
++i; |
} else { |
@@ -189,26 +196,58 @@ void RtpPacketizerH264::PacketizeFuA(size_t fragment_index) { |
// Fragment payload into packets (FU-A). |
// Strip out the original header and leave room for the FU-A header. |
const Fragment& fragment = input_fragments_[fragment_index]; |
- |
- size_t fragment_length = fragment.length - kNalHeaderSize; |
+ bool is_last_fragment = fragment_index + 1 == input_fragments_.size(); |
+ size_t payload_left = fragment.length - kNalHeaderSize; |
size_t offset = kNalHeaderSize; |
size_t bytes_available = max_payload_len_ - kFuAHeaderSize; |
- const size_t num_fragments = |
- (fragment_length + (bytes_available - 1)) / bytes_available; |
- |
- const size_t avg_size = (fragment_length + num_fragments - 1) / num_fragments; |
- while (fragment_length > 0) { |
- size_t packet_length = avg_size; |
- if (fragment_length < avg_size) |
- packet_length = fragment_length; |
+ |
+ // Instead of making the last packet smaller we pretend that all packets are |
+ // of the same size but we write additional virtual payload to the last |
+ // packet. |
+ size_t extra_len = is_last_fragment ? last_packet_reduction_len_ : 0; |
+ |
+ // Integer divisions with rounding up. Minimal number of packets to fit all |
+ // payload and virtual payload. |
+ size_t num_packets = |
+ (payload_left + extra_len + (bytes_available - 1)) / bytes_available; |
+ // Bytes per packet. |
+ size_t payload_per_packet = |
+ (payload_left + extra_len + num_packets - 1) / num_packets; |
sprang_webrtc
2017/05/17 13:10:07
nit: maybe (num_packets - 1) for consistency with
ilnik
2017/05/17 15:06:33
Done.
|
+ // We make several first packets to be 1 bytes larger than the rest. |
+ // i.e 14 bytes splitted in 4 packets would be 4+4+3+3. |
+ size_t num_smaller_packets = |
+ num_packets - (payload_left + extra_len) % num_packets; |
sprang_webrtc
2017/05/17 13:10:06
If you use num_larger_packets instead you can get
ilnik
2017/05/17 15:06:33
Done.
|
+ // If all the packets are of equal size, we assume they all are 1 byte larger. |
+ if (num_smaller_packets == num_packets) |
+ num_smaller_packets = 0; |
+ while (payload_left > 0) { |
+ // Reduce payload per packet at the right time. |
+ if (num_packets == num_smaller_packets) |
+ payload_per_packet--; |
+ size_t packet_length = payload_per_packet; |
+ if (payload_left <= packet_length) { // Last portion of the payload |
+ packet_length = payload_left; |
+ // One additional packet may be used for extensions in the last packet. |
+ // Together with last payload packet there may be at most 2 of them. |
+ RTC_DCHECK_LE(num_packets, 2); |
+ if (num_packets == 2) { |
+ // Whole payload fits in the first num_packets-1 packets but extra |
+ // packet is used for virtual payload. Leave at least one byte of data |
+ // for the last packet. |
+ packet_length = packet_length - 1; |
+ } |
+ } |
+ RTC_CHECK_GT(packet_length, 0); |
packets_.push(PacketUnit(Fragment(fragment.buffer + offset, packet_length), |
offset - kNalHeaderSize == 0, |
- fragment_length == packet_length, false, |
+ payload_left == packet_length, false, |
fragment.buffer[0])); |
offset += packet_length; |
- fragment_length -= packet_length; |
+ payload_left -= packet_length; |
+ total_packets_++; |
sprang_webrtc
2017/05/17 13:10:07
nit: just do total_packet_ += num_packets before t
ilnik
2017/05/17 15:06:33
Done.
|
+ num_packets--; |
sprang_webrtc
2017/05/17 13:10:07
nit: prefer pre-increments (here and otherwise)
ilnik
2017/05/17 15:06:33
Done.
|
} |
- RTC_CHECK_EQ(0, fragment_length); |
+ RTC_CHECK_EQ(0, payload_left); |
} |
size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { |
@@ -218,19 +257,17 @@ size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { |
size_t fragment_headers_length = 0; |
const Fragment* fragment = &input_fragments_[fragment_index]; |
RTC_CHECK_GE(payload_size_left, fragment->length); |
- while (payload_size_left >= fragment->length + fragment_headers_length) { |
+ total_packets_++; |
+ while (payload_size_left >= fragment->length + fragment_headers_length && |
+ (fragment_index + 1 < input_fragments_.size() || |
+ payload_size_left >= fragment->length + fragment_headers_length + |
+ last_packet_reduction_len_)) { |
RTC_CHECK_GT(fragment->length, 0); |
packets_.push(PacketUnit(*fragment, aggregated_fragments == 0, false, true, |
fragment->buffer[0])); |
payload_size_left -= fragment->length; |
payload_size_left -= fragment_headers_length; |
- // Next fragment. |
- ++fragment_index; |
- if (fragment_index == input_fragments_.size()) |
- break; |
- fragment = &input_fragments_[fragment_index]; |
- |
fragment_headers_length = kLengthFieldSize; |
// If we are going to try to aggregate more fragments into this packet |
// we need to add the STAP-A NALU header and a length field for the first |
@@ -238,7 +275,14 @@ size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { |
if (aggregated_fragments == 0) |
fragment_headers_length += kNalHeaderSize + kLengthFieldSize; |
++aggregated_fragments; |
+ |
+ // Next fragment. |
+ ++fragment_index; |
+ if (fragment_index == input_fragments_.size()) |
+ break; |
+ fragment = &input_fragments_[fragment_index]; |
} |
+ RTC_CHECK_GT(aggregated_fragments, 0); |
packets_.back().last_fragment = true; |
return fragment_index; |
} |
@@ -246,6 +290,8 @@ size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { |
void RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) { |
// Add a single NALU to the queue, no aggregation. |
size_t payload_size_left = max_payload_len_; |
+ if (fragment_index + 1 == input_fragments_.size()) |
+ payload_size_left -= last_packet_reduction_len_; |
const Fragment* fragment = &input_fragments_[fragment_index]; |
RTC_CHECK_GE(payload_size_left, fragment->length) |
<< "Payload size left " << payload_size_left << ", fragment length " |
@@ -253,14 +299,12 @@ void RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) { |
RTC_CHECK_GT(fragment->length, 0u); |
packets_.push(PacketUnit(*fragment, true /* first */, true /* last */, |
false /* aggregated */, fragment->buffer[0])); |
+ total_packets_++; |
} |
-bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet, |
- bool* last_packet) { |
+bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet) { |
RTC_DCHECK(rtp_packet); |
- RTC_DCHECK(last_packet); |
if (packets_.empty()) { |
- *last_packet = true; |
return false; |
} |
@@ -274,19 +318,25 @@ bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet, |
input_fragments_.pop_front(); |
} else if (packet.aggregated) { |
RTC_CHECK_EQ(H264PacketizationMode::NonInterleaved, packetization_mode_); |
- NextAggregatePacket(rtp_packet); |
+ NextAggregatePacket(rtp_packet, total_packets_ == 1); |
} else { |
RTC_CHECK_EQ(H264PacketizationMode::NonInterleaved, packetization_mode_); |
NextFragmentPacket(rtp_packet); |
} |
RTC_DCHECK_LE(rtp_packet->payload_size(), max_payload_len_); |
- *last_packet = packets_.empty(); |
- rtp_packet->SetMarker(*last_packet); |
+ if (packets_.empty()) { |
+ RTC_DCHECK_LE(rtp_packet->payload_size(), |
+ max_payload_len_ - last_packet_reduction_len_); |
+ } |
+ rtp_packet->SetMarker(packets_.empty()); |
+ total_packets_--; |
return true; |
} |
-void RtpPacketizerH264::NextAggregatePacket(RtpPacketToSend* rtp_packet) { |
- uint8_t* buffer = rtp_packet->AllocatePayload(max_payload_len_); |
+void RtpPacketizerH264::NextAggregatePacket(RtpPacketToSend* rtp_packet, |
+ bool last) { |
+ uint8_t* buffer = rtp_packet->AllocatePayload( |
+ last ? max_payload_len_ - last_packet_reduction_len_ : max_payload_len_); |
RTC_DCHECK(buffer); |
PacketUnit* packet = &packets_.front(); |
RTC_CHECK(packet->first_fragment); |