| Index: webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
|
| diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
|
| index 38450742be9e29022cbabd2245952a88a1b09ddf..4e46b78baa6090fd98c68529b6d1a9700fa735fc 100644
|
| --- a/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
|
| +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
|
| @@ -13,9 +13,10 @@
|
| #include <stdlib.h>
|
| #include <string.h>
|
|
|
| +#include <limits>
|
| #include <memory>
|
| -#include <vector>
|
| #include <utility>
|
| +#include <vector>
|
|
|
| #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
| #include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
| @@ -33,6 +34,7 @@ namespace webrtc {
|
|
|
| namespace {
|
| constexpr size_t kRedForFecHeaderLength = 1;
|
| +constexpr int64_t kMaxUnretransmittableFrameIntervalMs = 33 * 4;
|
|
|
| void BuildRedPayload(const RtpPacketToSend& media_packet,
|
| RtpPacketToSend* red_packet) {
|
| @@ -53,7 +55,8 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock,
|
| : rtp_sender_(rtp_sender),
|
| clock_(clock),
|
| video_type_(kRtpVideoGeneric),
|
| - retransmission_settings_(kRetransmitBaseLayer),
|
| + retransmission_settings_(kRetransmitBaseLayer |
|
| + kConditionallyRetransmitHigherLayers),
|
| last_rotation_(kVideoRotation_0),
|
| red_payload_type_(-1),
|
| ulpfec_payload_type_(-1),
|
| @@ -292,7 +295,8 @@ bool RTPSenderVideo::SendVideo(RtpVideoCodecTypes video_type,
|
| const uint8_t* payload_data,
|
| size_t payload_size,
|
| const RTPFragmentationHeader* fragmentation,
|
| - const RTPVideoHeader* video_header) {
|
| + const RTPVideoHeader* video_header,
|
| + int64_t expected_retransmission_time_ms) {
|
| if (payload_size == 0)
|
| return false;
|
|
|
| @@ -365,8 +369,11 @@ bool RTPSenderVideo::SendVideo(RtpVideoCodecTypes video_type,
|
| std::unique_ptr<RtpPacketizer> packetizer(RtpPacketizer::Create(
|
| video_type, max_data_payload_length, last_packet_reduction_len,
|
| video_header ? &(video_header->codecHeader) : nullptr, frame_type));
|
| - // Media packet storage.
|
| - StorageType storage = packetizer->GetStorageType(retransmission_settings);
|
| +
|
| + const uint8_t temporal_id =
|
| + video_header ? GetTemporalId(*video_header) : kNoTemporalIdx;
|
| + StorageType storage = GetStorageType(temporal_id, retransmission_settings,
|
| + expected_retransmission_time_ms);
|
|
|
| // TODO(changbin): we currently don't support to configure the codec to
|
| // output multiple partitions for VP8. Should remove below check after the
|
| @@ -392,7 +399,9 @@ bool RTPSenderVideo::SendVideo(RtpVideoCodecTypes video_type,
|
| if (!rtp_sender_->AssignSequenceNumber(packet.get()))
|
| return false;
|
|
|
| - bool protect_packet = (packetizer->GetProtectionType() == kProtectedPacket);
|
| + // No FEC protection for upper temporal layers, if used.
|
| + bool protect_packet = temporal_id == 0 || temporal_id == kNoTemporalIdx;
|
| +
|
| // Put packetization finish timestamp into extension.
|
| if (packet->HasExtension<VideoTimingExtension>()) {
|
| packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds());
|
| @@ -453,4 +462,90 @@ void RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) {
|
| retransmission_settings_ = settings;
|
| }
|
|
|
| +StorageType RTPSenderVideo::GetStorageType(
|
| + uint8_t temporal_id,
|
| + int32_t retransmission_settings,
|
| + int64_t expected_retransmission_time_ms) {
|
| + if (retransmission_settings == kRetransmitOff)
|
| + return StorageType::kDontRetransmit;
|
| + if (retransmission_settings == kRetransmitAllPackets)
|
| + return StorageType::kAllowRetransmission;
|
| +
|
| + rtc::CritScope cs(&stats_crit_);
|
| + // Media packet storage.
|
| + if ((retransmission_settings & kConditionallyRetransmitHigherLayers) &&
|
| + UpdateConditionalRetransmit(temporal_id,
|
| + expected_retransmission_time_ms)) {
|
| + retransmission_settings |= kRetransmitHigherLayers;
|
| + }
|
| +
|
| + if (temporal_id == kNoTemporalIdx)
|
| + return kAllowRetransmission;
|
| +
|
| + if ((retransmission_settings & kRetransmitBaseLayer) && temporal_id == 0)
|
| + return kAllowRetransmission;
|
| +
|
| + if ((retransmission_settings & kRetransmitHigherLayers) && temporal_id > 0)
|
| + return kAllowRetransmission;
|
| +
|
| + return kDontRetransmit;
|
| +}
|
| +
|
| +uint8_t RTPSenderVideo::GetTemporalId(const RTPVideoHeader& header) {
|
| + switch (header.codec) {
|
| + case kRtpVideoVp8:
|
| + return header.codecHeader.VP8.temporalIdx;
|
| + case kRtpVideoVp9:
|
| + return header.codecHeader.VP9.temporal_idx;
|
| + default:
|
| + return kNoTemporalIdx;
|
| + }
|
| +}
|
| +
|
| +bool RTPSenderVideo::UpdateConditionalRetransmit(
|
| + uint8_t temporal_id,
|
| + int64_t expected_retransmission_time_ms) {
|
| + int64_t now_ms = clock_->TimeInMilliseconds();
|
| + // Update stats for any temporal layer.
|
| + TemporalLayerStats* current_layer_stats =
|
| + &frame_stats_by_temporal_layer_[temporal_id];
|
| + current_layer_stats->frame_rate_fp1000s.Update(1, now_ms);
|
| + int64_t tl_frame_interval = now_ms - current_layer_stats->last_frame_time_ms;
|
| + current_layer_stats->last_frame_time_ms = now_ms;
|
| +
|
| + // Conditional retransmit only applies to upper layers.
|
| + if (temporal_id != kNoTemporalIdx && temporal_id > 0) {
|
| + if (tl_frame_interval >= kMaxUnretransmittableFrameIntervalMs) {
|
| + // Too long since a retransmittable frame in this layer, enable NACK
|
| + // protection.
|
| + return true;
|
| + } else {
|
| + // Estimate when the next frame of any lower layer will be sent.
|
| + const int64_t kUndefined = std::numeric_limits<int64_t>::max();
|
| + int64_t expected_next_frame_time = kUndefined;
|
| + for (int i = temporal_id - 1; i >= 0; --i) {
|
| + TemporalLayerStats* stats = &frame_stats_by_temporal_layer_[i];
|
| + rtc::Optional<uint32_t> rate = stats->frame_rate_fp1000s.Rate(now_ms);
|
| + if (rate) {
|
| + int64_t tl_next = stats->last_frame_time_ms + 1000000 / *rate;
|
| + if (tl_next - now_ms > -expected_retransmission_time_ms &&
|
| + tl_next < expected_next_frame_time) {
|
| + expected_next_frame_time = tl_next;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (expected_next_frame_time == kUndefined ||
|
| + expected_next_frame_time - now_ms > expected_retransmission_time_ms) {
|
| + // The next frame in a lower layer is expected at a later time (or
|
| + // unable to tell due to lack of data) than a retransmission is
|
| + // estimated to be able to arrive, so allow this packet to be nacked.
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| } // namespace webrtc
|
|
|