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..da88c9c1c3c35ffcbb620bf55c4ea5e230bcfb65 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,10 @@ 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); |
+ |
+ StorageType storage = |
+ GetStorageType(*video_header, retransmission_settings, packetizer.get(), |
+ 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 |
@@ -453,4 +459,92 @@ void RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) { |
retransmission_settings_ = settings; |
} |
+RTPSenderVideo::TemporalLayerStats* RTPSenderVideo::GetTemporalLayerStats( |
danilchap
2017/08/29 17:31:20
since return type can't be nullptr, may return ref
sprang_webrtc
2017/08/31 15:54:29
Are we ok with non-const references then?
Changed
danilchap
2017/09/04 09:03:04
the rule states "All parameters passed by referenc
sprang_webrtc
2017/09/04 11:09:28
Acknowledged.
|
+ int temporal_id) { |
+ auto it = frame_stats_by_temporal_layer_.find(temporal_id); |
+ if (it == frame_stats_by_temporal_layer_.end()) { |
+ const int kWindowSizeMs = 2500; |
+ it = frame_stats_by_temporal_layer_.emplace(temporal_id, kWindowSizeMs) |
danilchap
2017/08/29 17:31:20
you do not need to search before emplace, so can r
sprang_webrtc
2017/08/31 15:54:29
Acknowledged.
|
+ .first; |
+ } |
+ return &it->second; |
+} |
+ |
+StorageType RTPSenderVideo::GetStorageType( |
danilchap
2017/08/30 13:52:29
Looks strange to have GetStorageType both in codec
sprang_webrtc
2017/08/31 15:54:29
Yeah, that makes sense. Moved things around.
|
+ const RTPVideoHeader& header, |
+ int32_t retransmission_settings, |
+ RtpPacketizer* packetizer, |
+ int64_t expected_retransmission_time) { |
+ if (retransmission_settings == kRetransmitOff) |
+ return StorageType::kDontRetransmit; |
+ if (retransmission_settings == kRetransmitAllPackets) |
+ return StorageType::kAllowRetransmission; |
+ |
+ rtc::CritScope cs(&stats_crit_); |
+ // Media packet storage. |
+ int64_t now_ms = clock_->TimeInMilliseconds(); |
+ int pic_id = kNoPictureId; |
+ if (retransmission_settings & kConditionallyRetransmitHigherLayers) { |
+ int temporal_layer = 0; |
danilchap
2017/08/30 13:52:29
may be = kNoTemporalIdx
sprang_webrtc
2017/08/31 15:54:29
Done.
|
+ switch (header.codec) { |
+ case kRtpVideoVp8: |
+ temporal_layer = header.codecHeader.VP8.temporalIdx; |
+ pic_id = header.codecHeader.VP8.pictureId; |
+ break; |
+ case kRtpVideoVp9: |
+ temporal_layer = header.codecHeader.VP9.temporal_idx; |
+ pic_id = header.codecHeader.VP9.picture_id; |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ // Update stats for any temporal layer. |
+ TemporalLayerStats* current_layer_stats = |
+ GetTemporalLayerStats(temporal_layer); |
+ current_layer_stats->frame_rate_fp1000s_.Update(1, now_ms); |
+ int64_t tl_frame_interval = now_ms - current_layer_stats->last_frame_time_; |
+ current_layer_stats->last_frame_time_ = now_ms; |
+ |
+ // Conditional retransmit only applies to upper layers. |
+ if (temporal_layer != kNoTemporalIdx && temporal_layer > 0) { |
+ bool enable_retransmission = false; |
+ if (tl_frame_interval >= kMaxUnretransmittableFrameIntervalMs) { |
+ // Too long since a retransmittable frame in this layer, enable NACK |
+ // protection. |
+ enable_retransmission = 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_layer - 1; i >= 0; --i) { |
+ TemporalLayerStats* stats = GetTemporalLayerStats(i); |
+ rtc::Optional<uint32_t> rate = |
+ stats->frame_rate_fp1000s_.Rate(now_ms); |
+ if (rate) { |
+ int64_t tl_next = stats->last_frame_time_ + 1000000 / *rate; |
+ if (tl_next - now_ms > -expected_retransmission_time && |
+ 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) { |
+ // 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. |
+ enable_retransmission = true; |
+ } |
+ } |
+ |
+ if (enable_retransmission) |
+ retransmission_settings |= kRetransmitHigherLayers; |
danilchap
2017/08/29 17:31:20
you already checked temporal_layer > 0.
why not re
sprang_webrtc
2017/08/31 15:54:29
Since this class doesn't know of the codec depende
|
+ } |
+ } |
+ |
+ return packetizer->GetStorageType(retransmission_settings); |
+} |
+ |
} // namespace webrtc |