niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 1 | /* |
pwestin@webrtc.org | f6bb77a | 2012-01-24 17:16:59 | [diff] [blame] | 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 3 | * |
| 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 |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 11 | #include "modules/rtp_rtcp/source/rtp_sender_video.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 12 | |
pbos@webrtc.org | a048d7c | 2013-05-29 14:27:38 | [diff] [blame] | 13 | #include <stdlib.h> |
| 14 | #include <string.h> |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 15 | |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 16 | #include <limits> |
kwiberg | 84be511 | 2016-04-27 08:19:58 | [diff] [blame] | 17 | #include <memory> |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 18 | #include <utility> |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 19 | #include <vector> |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 20 | |
Karl Wiberg | 918f50c | 2018-07-05 09:40:33 | [diff] [blame] | 21 | #include "absl/memory/memory.h" |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 22 | #include "absl/strings/match.h" |
Benjamin Wright | 192eeec | 2018-10-18 00:27:25 | [diff] [blame] | 23 | #include "api/crypto/frameencryptorinterface.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 24 | #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" |
| 25 | #include "modules/rtp_rtcp/source/byte_io.h" |
| 26 | #include "modules/rtp_rtcp/source/rtp_format_video_generic.h" |
| 27 | #include "modules/rtp_rtcp/source/rtp_format_vp8.h" |
| 28 | #include "modules/rtp_rtcp/source/rtp_format_vp9.h" |
philipel | b3e42a4 | 2018-09-13 08:57:14 | [diff] [blame] | 29 | #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 30 | #include "modules/rtp_rtcp/source/rtp_header_extensions.h" |
| 31 | #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" |
| 32 | #include "rtc_base/checks.h" |
| 33 | #include "rtc_base/logging.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 34 | #include "rtc_base/trace_event.h" |
ilnik | ed9b9ff | 2017-06-02 14:30:20 | [diff] [blame] | 35 | |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 36 | namespace webrtc { |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 37 | |
brandtr | 6631e8a | 2016-09-13 10:23:29 | [diff] [blame] | 38 | namespace { |
| 39 | constexpr size_t kRedForFecHeaderLength = 1; |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 40 | constexpr int64_t kMaxUnretransmittableFrameIntervalMs = 33 * 4; |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 41 | |
| 42 | void BuildRedPayload(const RtpPacketToSend& media_packet, |
| 43 | RtpPacketToSend* red_packet) { |
| 44 | uint8_t* red_payload = red_packet->AllocatePayload( |
| 45 | kRedForFecHeaderLength + media_packet.payload_size()); |
| 46 | RTC_DCHECK(red_payload); |
| 47 | red_payload[0] = media_packet.PayloadType(); |
danilchap | 96c1587 | 2016-11-21 09:35:29 | [diff] [blame] | 48 | |
| 49 | auto media_payload = media_packet.payload(); |
| 50 | memcpy(&red_payload[kRedForFecHeaderLength], media_payload.data(), |
| 51 | media_payload.size()); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 52 | } |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 53 | |
| 54 | void AddRtpHeaderExtensions(const RTPVideoHeader& video_header, |
| 55 | FrameType frame_type, |
| 56 | bool set_video_rotation, |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 57 | bool set_color_space, |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 58 | bool first_packet, |
| 59 | bool last_packet, |
| 60 | RtpPacketToSend* packet) { |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 61 | // Color space requires two-byte header extensions if HDR metadata is |
| 62 | // included. Therefore, it's best to add this extension first so that the |
| 63 | // other extensions in the same packet are written as two-byte headers at |
| 64 | // once. |
| 65 | if (last_packet && set_color_space && video_header.color_space) |
| 66 | packet->SetExtension<ColorSpaceExtension>(video_header.color_space.value()); |
| 67 | |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 68 | if (last_packet && set_video_rotation) |
| 69 | packet->SetExtension<VideoOrientation>(video_header.rotation); |
| 70 | |
| 71 | // Report content type only for key frames. |
| 72 | if (last_packet && frame_type == kVideoFrameKey && |
| 73 | video_header.content_type != VideoContentType::UNSPECIFIED) |
| 74 | packet->SetExtension<VideoContentTypeExtension>(video_header.content_type); |
| 75 | |
| 76 | if (last_packet && |
| 77 | video_header.video_timing.flags != VideoSendTiming::kInvalid) |
| 78 | packet->SetExtension<VideoTimingExtension>(video_header.video_timing); |
philipel | b3e42a4 | 2018-09-13 08:57:14 | [diff] [blame] | 79 | |
| 80 | if (video_header.generic) { |
| 81 | RtpGenericFrameDescriptor generic_descriptor; |
| 82 | generic_descriptor.SetFirstPacketInSubFrame(first_packet); |
| 83 | generic_descriptor.SetLastPacketInSubFrame(last_packet); |
| 84 | generic_descriptor.SetFirstSubFrameInFrame(true); |
| 85 | generic_descriptor.SetLastSubFrameInFrame(true); |
| 86 | |
| 87 | if (first_packet) { |
| 88 | generic_descriptor.SetFrameId( |
| 89 | static_cast<uint16_t>(video_header.generic->frame_id)); |
| 90 | for (int64_t dep : video_header.generic->dependencies) { |
| 91 | generic_descriptor.AddFrameDependencyDiff( |
| 92 | video_header.generic->frame_id - dep); |
| 93 | } |
| 94 | |
| 95 | uint8_t spatial_bimask = 1 << video_header.generic->spatial_index; |
| 96 | for (int layer : video_header.generic->higher_spatial_layers) { |
| 97 | RTC_DCHECK_GT(layer, video_header.generic->spatial_index); |
| 98 | RTC_DCHECK_LT(layer, 8); |
| 99 | spatial_bimask |= 1 << layer; |
| 100 | } |
| 101 | generic_descriptor.SetSpatialLayersBitmask(spatial_bimask); |
| 102 | |
| 103 | generic_descriptor.SetTemporalLayer(video_header.generic->temporal_index); |
philipel | fab9129 | 2018-10-17 12:36:08 | [diff] [blame] | 104 | |
| 105 | if (frame_type == kVideoFrameKey) { |
| 106 | generic_descriptor.SetResolution(video_header.width, |
| 107 | video_header.height); |
| 108 | } |
philipel | b3e42a4 | 2018-09-13 08:57:14 | [diff] [blame] | 109 | } |
| 110 | packet->SetExtension<RtpGenericFrameDescriptorExtension>( |
| 111 | generic_descriptor); |
| 112 | } |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 113 | } |
| 114 | |
Danil Chapovalov | 84ffb35 | 2018-09-25 16:59:09 | [diff] [blame] | 115 | bool MinimizeDescriptor(const RTPVideoHeader& full, RTPVideoHeader* minimized) { |
| 116 | if (full.codec == VideoCodecType::kVideoCodecVP8) { |
| 117 | minimized->codec = VideoCodecType::kVideoCodecVP8; |
| 118 | const auto& vp8 = absl::get<RTPVideoHeaderVP8>(full.video_type_header); |
| 119 | // Set minimum fields the RtpPacketizer is using to create vp8 packets. |
| 120 | auto& min_vp8 = minimized->video_type_header.emplace<RTPVideoHeaderVP8>(); |
| 121 | min_vp8.InitRTPVideoHeaderVP8(); |
| 122 | min_vp8.nonReference = vp8.nonReference; |
| 123 | return true; |
| 124 | } |
| 125 | // TODO(danilchap): Reduce vp9 codec specific descriptor too. |
| 126 | return false; |
| 127 | } |
| 128 | |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 129 | bool IsBaseLayer(const RTPVideoHeader& video_header) { |
| 130 | switch (video_header.codec) { |
| 131 | case kVideoCodecVP8: { |
| 132 | const auto& vp8 = |
| 133 | absl::get<RTPVideoHeaderVP8>(video_header.video_type_header); |
| 134 | return (vp8.temporalIdx == 0 || vp8.temporalIdx == kNoTemporalIdx); |
| 135 | } |
| 136 | case kVideoCodecVP9: { |
| 137 | const auto& vp9 = |
| 138 | absl::get<RTPVideoHeaderVP9>(video_header.video_type_header); |
| 139 | return (vp9.temporal_idx == 0 || vp9.temporal_idx == kNoTemporalIdx); |
| 140 | } |
| 141 | case kVideoCodecH264: |
| 142 | // TODO(kron): Implement logic for H264 once WebRTC supports temporal |
| 143 | // layers for H264. |
| 144 | break; |
| 145 | default: |
| 146 | break; |
| 147 | } |
| 148 | return true; |
| 149 | } |
| 150 | |
brandtr | 6631e8a | 2016-09-13 10:23:29 | [diff] [blame] | 151 | } // namespace |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 152 | |
brandtr | dbdb3f1 | 2016-11-10 13:04:48 | [diff] [blame] | 153 | RTPSenderVideo::RTPSenderVideo(Clock* clock, |
| 154 | RTPSender* rtp_sender, |
Benjamin Wright | 192eeec | 2018-10-18 00:27:25 | [diff] [blame] | 155 | FlexfecSender* flexfec_sender, |
| 156 | FrameEncryptorInterface* frame_encryptor, |
| 157 | bool require_frame_encryption) |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 158 | : rtp_sender_(rtp_sender), |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 159 | clock_(clock), |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 160 | video_type_(kVideoCodecGeneric), |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 161 | retransmission_settings_(kRetransmitBaseLayer | |
| 162 | kConditionallyRetransmitHigherLayers), |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 163 | last_rotation_(kVideoRotation_0), |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 164 | transmit_color_space_next_frame_(false), |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 165 | red_payload_type_(-1), |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 166 | ulpfec_payload_type_(-1), |
brandtr | dbdb3f1 | 2016-11-10 13:04:48 | [diff] [blame] | 167 | flexfec_sender_(flexfec_sender), |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 168 | delta_fec_params_{0, 1, kFecMaskRandom}, |
| 169 | key_fec_params_{0, 1, kFecMaskRandom}, |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 170 | fec_bitrate_(1000, RateStatistics::kBpsScale), |
Benjamin Wright | 192eeec | 2018-10-18 00:27:25 | [diff] [blame] | 171 | video_bitrate_(1000, RateStatistics::kBpsScale), |
| 172 | frame_encryptor_(frame_encryptor), |
| 173 | require_frame_encryption_(require_frame_encryption) {} |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 174 | |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 175 | RTPSenderVideo::~RTPSenderVideo() {} |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 176 | |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 177 | void RTPSenderVideo::SetVideoCodecType(enum VideoCodecType video_type) { |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 178 | video_type_ = video_type; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 179 | } |
| 180 | |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 181 | VideoCodecType RTPSenderVideo::VideoCodecType() const { |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 182 | return video_type_; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 183 | } |
| 184 | |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 185 | // Static. |
| 186 | RtpUtility::Payload* RTPSenderVideo::CreateVideoPayload( |
Niels Möller | f418bcb | 2018-11-05 12:27:35 | [diff] [blame] | 187 | absl::string_view payload_name, |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 188 | int8_t payload_type) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 189 | enum VideoCodecType video_type = kVideoCodecGeneric; |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 190 | if (absl::EqualsIgnoreCase(payload_name, "VP8")) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 191 | video_type = kVideoCodecVP8; |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 192 | } else if (absl::EqualsIgnoreCase(payload_name, "VP9")) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 193 | video_type = kVideoCodecVP9; |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 194 | } else if (absl::EqualsIgnoreCase(payload_name, "H264")) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 195 | video_type = kVideoCodecH264; |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 196 | } else if (absl::EqualsIgnoreCase(payload_name, "I420")) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 197 | video_type = kVideoCodecGeneric; |
Niels Möller | aa3c1cc | 2018-11-02 09:54:56 | [diff] [blame] | 198 | } else if (absl::EqualsIgnoreCase(payload_name, "stereo")) { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 199 | video_type = kVideoCodecGeneric; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 | [diff] [blame] | 200 | } else { |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 201 | video_type = kVideoCodecGeneric; |
pwestin@webrtc.org | 95cf479 | 2012-01-20 06:59:06 | [diff] [blame] | 202 | } |
Karl Wiberg | 83d3ec1 | 2017-09-28 17:54:38 | [diff] [blame] | 203 | VideoPayload vp; |
| 204 | vp.videoCodecType = video_type; |
| 205 | return new RtpUtility::Payload(payload_name, PayloadUnion(vp)); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 206 | } |
| 207 | |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 208 | void RTPSenderVideo::SendVideoPacket(std::unique_ptr<RtpPacketToSend> packet, |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 209 | StorageType storage) { |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 210 | // Remember some values about the packet before sending it away. |
| 211 | size_t packet_size = packet->size(); |
| 212 | uint16_t seq_num = packet->SequenceNumber(); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 213 | if (!rtp_sender_->SendToNetwork(std::move(packet), storage, |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 214 | RtpPacketSender::kLowPriority)) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 215 | RTC_LOG(LS_WARNING) << "Failed to send video packet " << seq_num; |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 216 | return; |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 217 | } |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 218 | rtc::CritScope cs(&stats_crit_); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 219 | video_bitrate_.Update(packet_size, clock_->TimeInMilliseconds()); |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 220 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 221 | |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 222 | void RTPSenderVideo::SendVideoPacketAsRedMaybeWithUlpfec( |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 223 | std::unique_ptr<RtpPacketToSend> media_packet, |
| 224 | StorageType media_packet_storage, |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 225 | bool protect_media_packet) { |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 226 | uint16_t media_seq_num = media_packet->SequenceNumber(); |
| 227 | |
| 228 | std::unique_ptr<RtpPacketToSend> red_packet( |
| 229 | new RtpPacketToSend(*media_packet)); |
| 230 | BuildRedPayload(*media_packet, red_packet.get()); |
| 231 | |
brandtr | 74811e5 | 2016-08-10 07:51:50 | [diff] [blame] | 232 | std::vector<std::unique_ptr<RedPacket>> fec_packets; |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 233 | StorageType fec_storage = kDontRetransmit; |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 234 | { |
| 235 | // Only protect while creating RED and FEC packets, not when sending. |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 236 | rtc::CritScope cs(&crit_); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 237 | red_packet->SetPayloadType(red_payload_type_); |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 238 | if (ulpfec_enabled()) { |
| 239 | if (protect_media_packet) { |
| 240 | ulpfec_generator_.AddRtpPacketAndGenerateFec( |
| 241 | media_packet->data(), media_packet->payload_size(), |
| 242 | media_packet->headers_size()); |
| 243 | } |
| 244 | uint16_t num_fec_packets = ulpfec_generator_.NumAvailableFecPackets(); |
| 245 | if (num_fec_packets > 0) { |
| 246 | uint16_t first_fec_sequence_number = |
| 247 | rtp_sender_->AllocateSequenceNumber(num_fec_packets); |
| 248 | fec_packets = ulpfec_generator_.GetUlpfecPacketsAsRed( |
Rasmus Brandt | 393e266 | 2018-01-22 11:52:36 | [diff] [blame] | 249 | red_payload_type_, ulpfec_payload_type_, first_fec_sequence_number); |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 250 | RTC_DCHECK_EQ(num_fec_packets, fec_packets.size()); |
| 251 | if (retransmission_settings_ & kRetransmitFECPackets) |
| 252 | fec_storage = kAllowRetransmission; |
| 253 | } |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 254 | } |
| 255 | } |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 256 | // Send |red_packet| instead of |packet| for allocated sequence number. |
| 257 | size_t red_packet_size = red_packet->size(); |
| 258 | if (rtp_sender_->SendToNetwork(std::move(red_packet), media_packet_storage, |
| 259 | RtpPacketSender::kLowPriority)) { |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 260 | rtc::CritScope cs(&stats_crit_); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 261 | video_bitrate_.Update(red_packet_size, clock_->TimeInMilliseconds()); |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 262 | } else { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 263 | RTC_LOG(LS_WARNING) << "Failed to send RED packet " << media_seq_num; |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 264 | } |
brandtr | 74811e5 | 2016-08-10 07:51:50 | [diff] [blame] | 265 | for (const auto& fec_packet : fec_packets) { |
brandtr | 869e7cd | 2016-10-31 12:27:07 | [diff] [blame] | 266 | // TODO(danilchap): Make ulpfec_generator_ generate RtpPacketToSend to avoid |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 267 | // reparsing them. |
| 268 | std::unique_ptr<RtpPacketToSend> rtp_packet( |
| 269 | new RtpPacketToSend(*media_packet)); |
| 270 | RTC_CHECK(rtp_packet->Parse(fec_packet->data(), fec_packet->length())); |
| 271 | rtp_packet->set_capture_time_ms(media_packet->capture_time_ms()); |
| 272 | uint16_t fec_sequence_number = rtp_packet->SequenceNumber(); |
| 273 | if (rtp_sender_->SendToNetwork(std::move(rtp_packet), fec_storage, |
| 274 | RtpPacketSender::kLowPriority)) { |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 275 | rtc::CritScope cs(&stats_crit_); |
| 276 | fec_bitrate_.Update(fec_packet->length(), clock_->TimeInMilliseconds()); |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 277 | } else { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 278 | RTC_LOG(LS_WARNING) << "Failed to send ULPFEC packet " |
| 279 | << fec_sequence_number; |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 280 | } |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | void RTPSenderVideo::SendVideoPacketWithFlexfec( |
| 285 | std::unique_ptr<RtpPacketToSend> media_packet, |
| 286 | StorageType media_packet_storage, |
| 287 | bool protect_media_packet) { |
| 288 | RTC_DCHECK(flexfec_sender_); |
| 289 | |
| 290 | if (protect_media_packet) |
| 291 | flexfec_sender_->AddRtpPacketAndGenerateFec(*media_packet); |
| 292 | |
| 293 | SendVideoPacket(std::move(media_packet), media_packet_storage); |
| 294 | |
| 295 | if (flexfec_sender_->FecAvailable()) { |
| 296 | std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets = |
| 297 | flexfec_sender_->GetFecPackets(); |
| 298 | for (auto& fec_packet : fec_packets) { |
brandtr | 81eab61 | 2017-01-24 12:06:09 | [diff] [blame] | 299 | size_t packet_length = fec_packet->size(); |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 300 | uint16_t seq_num = fec_packet->SequenceNumber(); |
| 301 | if (rtp_sender_->SendToNetwork(std::move(fec_packet), kDontRetransmit, |
| 302 | RtpPacketSender::kLowPriority)) { |
brandtr | 81eab61 | 2017-01-24 12:06:09 | [diff] [blame] | 303 | rtc::CritScope cs(&stats_crit_); |
| 304 | fec_bitrate_.Update(packet_length, clock_->TimeInMilliseconds()); |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 305 | } else { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 306 | RTC_LOG(LS_WARNING) << "Failed to send FlexFEC packet " << seq_num; |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 307 | } |
stefan@webrtc.org | fbea4e5 | 2011-10-27 16:08:29 | [diff] [blame] | 308 | } |
stefan@webrtc.org | e0d6fa4 | 2012-03-20 22:10:56 | [diff] [blame] | 309 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 310 | } |
| 311 | |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 312 | void RTPSenderVideo::SetUlpfecConfig(int red_payload_type, |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 313 | int ulpfec_payload_type) { |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 314 | // Sanity check. Per the definition of UlpfecConfig (see config.h), |
| 315 | // a payload type of -1 means that the corresponding feature is |
| 316 | // turned off. |
| 317 | RTC_DCHECK_GE(red_payload_type, -1); |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 318 | RTC_DCHECK_LE(red_payload_type, 127); |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 319 | RTC_DCHECK_GE(ulpfec_payload_type, -1); |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 320 | RTC_DCHECK_LE(ulpfec_payload_type, 127); |
| 321 | |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 322 | rtc::CritScope cs(&crit_); |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 323 | red_payload_type_ = red_payload_type; |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 324 | ulpfec_payload_type_ = ulpfec_payload_type; |
| 325 | |
| 326 | // Must not enable ULPFEC without RED. |
Kári Tristan Helgason | 798ee75 | 2018-07-11 14:04:57 | [diff] [blame] | 327 | RTC_DCHECK(!(red_enabled() ^ ulpfec_enabled())); |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 328 | |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 329 | // Reset FEC parameters. |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 330 | delta_fec_params_ = FecProtectionParams{0, 1, kFecMaskRandom}; |
| 331 | key_fec_params_ = FecProtectionParams{0, 1, kFecMaskRandom}; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 332 | } |
| 333 | |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 334 | void RTPSenderVideo::GetUlpfecConfig(int* red_payload_type, |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 335 | int* ulpfec_payload_type) const { |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 336 | rtc::CritScope cs(&crit_); |
brandtr | d804895 | 2016-11-07 10:08:51 | [diff] [blame] | 337 | *red_payload_type = red_payload_type_; |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 338 | *ulpfec_payload_type = ulpfec_payload_type_; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 339 | } |
| 340 | |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 341 | size_t RTPSenderVideo::CalculateFecPacketOverhead() const { |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 342 | if (flexfec_enabled()) |
| 343 | return flexfec_sender_->MaxPacketOverhead(); |
| 344 | |
stefan | 8f4c77f | 2016-06-03 07:16:45 | [diff] [blame] | 345 | size_t overhead = 0; |
brandtr | f1bb476 | 2016-11-07 11:05:06 | [diff] [blame] | 346 | if (red_enabled()) { |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 347 | // The RED overhead is due to a small header. |
| 348 | overhead += kRedForFecHeaderLength; |
| 349 | } |
| 350 | if (ulpfec_enabled()) { |
| 351 | // For ULPFEC, the overhead is the FEC headers plus RED for FEC header |
| 352 | // (see above) plus anything in RTP header beyond the 12 bytes base header |
| 353 | // (CSRC list, extensions...) |
pbos@webrtc.org | b5e6bfc | 2014-09-12 11:05:55 | [diff] [blame] | 354 | // This reason for the header extensions to be included here is that |
| 355 | // from an FEC viewpoint, they are part of the payload to be protected. |
| 356 | // (The base RTP header is already protected by the FEC header.) |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 357 | overhead += ulpfec_generator_.MaxPacketOverhead() + |
| 358 | (rtp_sender_->RtpHeaderLength() - kRtpHeaderSize); |
pbos@webrtc.org | b5e6bfc | 2014-09-12 11:05:55 | [diff] [blame] | 359 | } |
stefan | 8f4c77f | 2016-06-03 07:16:45 | [diff] [blame] | 360 | return overhead; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 361 | } |
| 362 | |
brandtr | 1743a19 | 2016-11-07 11:36:05 | [diff] [blame] | 363 | void RTPSenderVideo::SetFecParameters(const FecProtectionParams& delta_params, |
| 364 | const FecProtectionParams& key_params) { |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 365 | rtc::CritScope cs(&crit_); |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 366 | delta_fec_params_ = delta_params; |
| 367 | key_fec_params_ = key_params; |
marpan@google.com | 80c5d7a | 2011-07-15 21:32:40 | [diff] [blame] | 368 | } |
| 369 | |
Danil Chapovalov | d264df5 | 2018-06-14 10:59:38 | [diff] [blame] | 370 | absl::optional<uint32_t> RTPSenderVideo::FlexfecSsrc() const { |
brandtr | 9dfff29 | 2016-11-14 13:14:50 | [diff] [blame] | 371 | if (flexfec_sender_) { |
Oskar Sundbom | 3419cf9 | 2017-11-16 09:55:48 | [diff] [blame] | 372 | return flexfec_sender_->ssrc(); |
brandtr | 9dfff29 | 2016-11-14 13:14:50 | [diff] [blame] | 373 | } |
Danil Chapovalov | d264df5 | 2018-06-14 10:59:38 | [diff] [blame] | 374 | return absl::nullopt; |
brandtr | 9dfff29 | 2016-11-14 13:14:50 | [diff] [blame] | 375 | } |
| 376 | |
Niels Möller | 520ca4e | 2018-06-04 09:14:38 | [diff] [blame] | 377 | bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type, |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 378 | FrameType frame_type, |
| 379 | int8_t payload_type, |
danilchap | e5b4141 | 2016-08-22 10:39:23 | [diff] [blame] | 380 | uint32_t rtp_timestamp, |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 381 | int64_t capture_time_ms, |
| 382 | const uint8_t* payload_data, |
| 383 | size_t payload_size, |
| 384 | const RTPFragmentationHeader* fragmentation, |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 385 | const RTPVideoHeader* video_header, |
| 386 | int64_t expected_retransmission_time_ms) { |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 387 | if (payload_size == 0) |
| 388 | return false; |
Danil Chapovalov | f7f8a1f | 2018-08-28 17:45:31 | [diff] [blame] | 389 | RTC_CHECK(video_header); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 | [diff] [blame] | 390 | |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 391 | size_t fec_packet_overhead; |
| 392 | bool red_enabled; |
| 393 | int32_t retransmission_settings; |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 394 | bool set_video_rotation; |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 395 | bool set_color_space = false; |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 396 | { |
kthelgason | 917a4ee | 2016-11-10 14:22:17 | [diff] [blame] | 397 | rtc::CritScope cs(&crit_); |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 398 | // According to |
| 399 | // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/ |
| 400 | // ts_126114v120700p.pdf Section 7.4.5: |
| 401 | // The MTSI client shall add the payload bytes as defined in this clause |
| 402 | // onto the last RTP packet in each group of packets which make up a key |
| 403 | // frame (I-frame or IDR frame in H.264 (AVC), or an IRAP picture in H.265 |
| 404 | // (HEVC)). The MTSI client may also add the payload bytes onto the last RTP |
| 405 | // packet in each group of packets which make up another type of frame |
| 406 | // (e.g. a P-Frame) only if the current value is different from the previous |
| 407 | // value sent. |
Danil Chapovalov | f7f8a1f | 2018-08-28 17:45:31 | [diff] [blame] | 408 | // Set rotation when key frame or when changed (to follow standard). |
| 409 | // Or when different from 0 (to follow current receiver implementation). |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 410 | set_video_rotation = frame_type == kVideoFrameKey || |
| 411 | video_header->rotation != last_rotation_ || |
| 412 | video_header->rotation != kVideoRotation_0; |
| 413 | last_rotation_ = video_header->rotation; |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 414 | |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 415 | // Send color space when changed or if the frame is a key frame. Keep |
| 416 | // sending color space information until the first base layer frame to |
| 417 | // guarantee that the information is retrieved by the receiver. |
| 418 | if (video_header->color_space != last_color_space_) { |
| 419 | last_color_space_ = video_header->color_space; |
| 420 | set_color_space = true; |
| 421 | transmit_color_space_next_frame_ = !IsBaseLayer(*video_header); |
| 422 | } else { |
| 423 | set_color_space = |
| 424 | frame_type == kVideoFrameKey || transmit_color_space_next_frame_; |
| 425 | transmit_color_space_next_frame_ = transmit_color_space_next_frame_ |
| 426 | ? !IsBaseLayer(*video_header) |
| 427 | : false; |
| 428 | } |
| 429 | |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 430 | // FEC settings. |
| 431 | const FecProtectionParams& fec_params = |
| 432 | frame_type == kVideoFrameKey ? key_fec_params_ : delta_fec_params_; |
| 433 | if (flexfec_enabled()) |
| 434 | flexfec_sender_->SetFecParameters(fec_params); |
| 435 | if (ulpfec_enabled()) |
| 436 | ulpfec_generator_.SetFecParameters(fec_params); |
| 437 | |
| 438 | fec_packet_overhead = CalculateFecPacketOverhead(); |
| 439 | red_enabled = this->red_enabled(); |
| 440 | retransmission_settings = retransmission_settings_; |
danilchap | c1600c5 | 2016-10-26 10:33:11 | [diff] [blame] | 441 | } |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 442 | |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 443 | // Maximum size of packet including rtp headers. |
| 444 | // Extra space left in case packet will be resent using fec or rtx. |
Danil Chapovalov | fa5ec8d | 2018-09-07 08:57:26 | [diff] [blame] | 445 | int packet_capacity = rtp_sender_->MaxRtpPacketSize() - fec_packet_overhead - |
| 446 | (rtp_sender_->RtxStatus() ? kRtxHeaderSize : 0); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 447 | |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 448 | std::unique_ptr<RtpPacketToSend> single_packet = |
| 449 | rtp_sender_->AllocatePacket(); |
| 450 | RTC_DCHECK_LE(packet_capacity, single_packet->capacity()); |
| 451 | single_packet->SetPayloadType(payload_type); |
| 452 | single_packet->SetTimestamp(rtp_timestamp); |
| 453 | single_packet->set_capture_time_ms(capture_time_ms); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 454 | |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 455 | auto first_packet = absl::make_unique<RtpPacketToSend>(*single_packet); |
| 456 | auto middle_packet = absl::make_unique<RtpPacketToSend>(*single_packet); |
| 457 | auto last_packet = absl::make_unique<RtpPacketToSend>(*single_packet); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 458 | // Simplest way to estimate how much extensions would occupy is to set them. |
| 459 | AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation, |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 460 | set_color_space, /*first=*/true, /*last=*/true, |
| 461 | single_packet.get()); |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 462 | AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation, |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 463 | set_color_space, /*first=*/true, /*last=*/false, |
| 464 | first_packet.get()); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 465 | AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation, |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 466 | set_color_space, /*first=*/false, /*last=*/false, |
| 467 | middle_packet.get()); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 468 | AddRtpHeaderExtensions(*video_header, frame_type, set_video_rotation, |
Johannes Kron | d0b69a8 | 2018-12-03 13:18:53 | [diff] [blame^] | 469 | set_color_space, /*first=*/false, /*last=*/true, |
| 470 | last_packet.get()); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 471 | |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 472 | RTC_DCHECK_GT(packet_capacity, single_packet->headers_size()); |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 473 | RTC_DCHECK_GT(packet_capacity, first_packet->headers_size()); |
| 474 | RTC_DCHECK_GT(packet_capacity, middle_packet->headers_size()); |
ilnik | 7a3006b | 2017-05-23 16:34:21 | [diff] [blame] | 475 | RTC_DCHECK_GT(packet_capacity, last_packet->headers_size()); |
Danil Chapovalov | fa5ec8d | 2018-09-07 08:57:26 | [diff] [blame] | 476 | RtpPacketizer::PayloadSizeLimits limits; |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 477 | limits.max_payload_len = packet_capacity - middle_packet->headers_size(); |
Danil Chapovalov | fa5ec8d | 2018-09-07 08:57:26 | [diff] [blame] | 478 | |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 479 | RTC_DCHECK_GE(single_packet->headers_size(), middle_packet->headers_size()); |
| 480 | limits.single_packet_reduction_len = |
| 481 | single_packet->headers_size() - middle_packet->headers_size(); |
| 482 | |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 483 | RTC_DCHECK_GE(first_packet->headers_size(), middle_packet->headers_size()); |
| 484 | limits.first_packet_reduction_len = |
| 485 | first_packet->headers_size() - middle_packet->headers_size(); |
| 486 | |
| 487 | RTC_DCHECK_GE(last_packet->headers_size(), middle_packet->headers_size()); |
Danil Chapovalov | fa5ec8d | 2018-09-07 08:57:26 | [diff] [blame] | 488 | limits.last_packet_reduction_len = |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 489 | last_packet->headers_size() - middle_packet->headers_size(); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 490 | |
Danil Chapovalov | 84ffb35 | 2018-09-25 16:59:09 | [diff] [blame] | 491 | RTPVideoHeader minimized_video_header; |
| 492 | const RTPVideoHeader* packetize_video_header = video_header; |
Benjamin Wright | 192eeec | 2018-10-18 00:27:25 | [diff] [blame] | 493 | rtc::ArrayView<const uint8_t> generic_descriptor_raw = |
| 494 | first_packet->GetRawExtension<RtpGenericFrameDescriptorExtension>(); |
| 495 | if (!generic_descriptor_raw.empty()) { |
| 496 | if (MinimizeDescriptor(*video_header, &minimized_video_header)) { |
| 497 | packetize_video_header = &minimized_video_header; |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | // TODO(benwright@webrtc.org) - Allocate enough to always encrypt inline. |
| 502 | rtc::Buffer encrypted_video_payload; |
| 503 | if (frame_encryptor_ != nullptr) { |
| 504 | if (generic_descriptor_raw.empty()) { |
| 505 | return false; |
| 506 | } |
| 507 | |
| 508 | const size_t max_ciphertext_size = |
| 509 | frame_encryptor_->GetMaxCiphertextByteSize(cricket::MEDIA_TYPE_VIDEO, |
| 510 | payload_size); |
| 511 | encrypted_video_payload.SetSize(max_ciphertext_size); |
| 512 | |
| 513 | size_t bytes_written = 0; |
| 514 | if (frame_encryptor_->Encrypt( |
| 515 | cricket::MEDIA_TYPE_VIDEO, first_packet->Ssrc(), |
| 516 | /*additional_data=*/nullptr, |
| 517 | rtc::MakeArrayView(payload_data, payload_size), |
| 518 | encrypted_video_payload, &bytes_written) != 0) { |
| 519 | return false; |
| 520 | } |
| 521 | |
| 522 | encrypted_video_payload.SetSize(bytes_written); |
| 523 | payload_data = encrypted_video_payload.data(); |
| 524 | payload_size = encrypted_video_payload.size(); |
| 525 | } else if (require_frame_encryption_) { |
| 526 | RTC_LOG(LS_WARNING) |
| 527 | << "No FrameEncryptor is attached to this video sending stream but " |
| 528 | << "one is required since require_frame_encryptor is set"; |
Danil Chapovalov | 84ffb35 | 2018-09-25 16:59:09 | [diff] [blame] | 529 | } |
| 530 | |
Danil Chapovalov | f7f8a1f | 2018-08-28 17:45:31 | [diff] [blame] | 531 | std::unique_ptr<RtpPacketizer> packetizer = RtpPacketizer::Create( |
| 532 | video_type, rtc::MakeArrayView(payload_data, payload_size), limits, |
Danil Chapovalov | 84ffb35 | 2018-09-25 16:59:09 | [diff] [blame] | 533 | *packetize_video_header, frame_type, fragmentation); |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 534 | |
Danil Chapovalov | f7f8a1f | 2018-08-28 17:45:31 | [diff] [blame] | 535 | const uint8_t temporal_id = GetTemporalId(*video_header); |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 536 | StorageType storage = GetStorageType(temporal_id, retransmission_settings, |
| 537 | expected_retransmission_time_ms); |
Danil Chapovalov | f7f8a1f | 2018-08-28 17:45:31 | [diff] [blame] | 538 | size_t num_packets = packetizer->NumPackets(); |
ilnik | 7a3006b | 2017-05-23 16:34:21 | [diff] [blame] | 539 | |
| 540 | if (num_packets == 0) |
| 541 | return false; |
pbos@webrtc.org | b5e6bfc | 2014-09-12 11:05:55 | [diff] [blame] | 542 | |
danilchap | 2a615fc | 2016-11-11 10:27:35 | [diff] [blame] | 543 | bool first_frame = first_frame_sent_(); |
ilnik | 7a3006b | 2017-05-23 16:34:21 | [diff] [blame] | 544 | for (size_t i = 0; i < num_packets; ++i) { |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 545 | std::unique_ptr<RtpPacketToSend> packet; |
| 546 | int expected_payload_capacity; |
| 547 | // Choose right packet template: |
| 548 | if (num_packets == 1) { |
Danil Chapovalov | 6026f05 | 2018-10-16 14:22:33 | [diff] [blame] | 549 | packet = std::move(single_packet); |
Danil Chapovalov | fcebe0e | 2018-10-12 15:51:22 | [diff] [blame] | 550 | expected_payload_capacity = |
| 551 | limits.max_payload_len - limits.single_packet_reduction_len; |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 552 | } else if (i == 0) { |
| 553 | packet = std::move(first_packet); |
| 554 | expected_payload_capacity = |
| 555 | limits.max_payload_len - limits.first_packet_reduction_len; |
| 556 | } else if (i == num_packets - 1) { |
| 557 | packet = std::move(last_packet); |
| 558 | expected_payload_capacity = |
| 559 | limits.max_payload_len - limits.last_packet_reduction_len; |
| 560 | } else { |
| 561 | packet = absl::make_unique<RtpPacketToSend>(*middle_packet); |
| 562 | expected_payload_capacity = limits.max_payload_len; |
| 563 | } |
| 564 | |
ilnik | 7a3006b | 2017-05-23 16:34:21 | [diff] [blame] | 565 | if (!packetizer->NextPacket(packet.get())) |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 566 | return false; |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 567 | RTC_DCHECK_LE(packet->payload_size(), expected_payload_capacity); |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 568 | if (!rtp_sender_->AssignSequenceNumber(packet.get())) |
| 569 | return false; |
| 570 | |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 571 | // No FEC protection for upper temporal layers, if used. |
| 572 | bool protect_packet = temporal_id == 0 || temporal_id == kNoTemporalIdx; |
| 573 | |
ilnik | 04f4d12 | 2017-06-19 14:18:55 | [diff] [blame] | 574 | // Put packetization finish timestamp into extension. |
ilnik | e435019 | 2017-06-29 09:27:44 | [diff] [blame] | 575 | if (packet->HasExtension<VideoTimingExtension>()) { |
ilnik | 04f4d12 | 2017-06-19 14:18:55 | [diff] [blame] | 576 | packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds()); |
ilnik | 1089499 | 2017-06-21 15:23:19 | [diff] [blame] | 577 | // TODO(ilnik): Due to webrtc:7859, packets with timing extensions are not |
| 578 | // protected by FEC. It reduces FEC efficiency a bit. When FEC is moved |
| 579 | // below the pacer, it can be re-enabled for these packets. |
| 580 | // NOTE: Any RTP stream processor in the network, modifying 'network' |
| 581 | // timestamps in the timing frames extension have to be an end-point for |
| 582 | // FEC, otherwise recovered by FEC packets will be corrupted. |
| 583 | protect_packet = false; |
ilnik | 04f4d12 | 2017-06-19 14:18:55 | [diff] [blame] | 584 | } |
| 585 | |
brandtr | 131bc49 | 2016-11-10 13:01:11 | [diff] [blame] | 586 | if (flexfec_enabled()) { |
| 587 | // TODO(brandtr): Remove the FlexFEC code path when FlexfecSender |
| 588 | // is wired up to PacedSender instead. |
| 589 | SendVideoPacketWithFlexfec(std::move(packet), storage, protect_packet); |
| 590 | } else if (red_enabled) { |
| 591 | SendVideoPacketAsRedMaybeWithUlpfec(std::move(packet), storage, |
| 592 | protect_packet); |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 593 | } else { |
danilchap | 7411061 | 2016-10-02 17:54:29 | [diff] [blame] | 594 | SendVideoPacket(std::move(packet), storage); |
stefan@webrtc.org | 2ec5606 | 2014-07-31 14:59:24 | [diff] [blame] | 595 | } |
skvlad | 98bb664 | 2016-04-07 22:36:45 | [diff] [blame] | 596 | |
| 597 | if (first_frame) { |
ilnik | 7a3006b | 2017-05-23 16:34:21 | [diff] [blame] | 598 | if (i == 0) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 599 | RTC_LOG(LS_INFO) |
skvlad | 98bb664 | 2016-04-07 22:36:45 | [diff] [blame] | 600 | << "Sent first RTP packet of the first video frame (pre-pacer)"; |
| 601 | } |
Danil Chapovalov | e4f8b38 | 2018-09-07 15:30:26 | [diff] [blame] | 602 | if (i == num_packets - 1) { |
Mirko Bonadei | 675513b | 2017-11-09 10:09:25 | [diff] [blame] | 603 | RTC_LOG(LS_INFO) |
skvlad | 98bb664 | 2016-04-07 22:36:45 | [diff] [blame] | 604 | << "Sent last RTP packet of the first video frame (pre-pacer)"; |
| 605 | } |
| 606 | } |
stefan@webrtc.org | 2ec5606 | 2014-07-31 14:59:24 | [diff] [blame] | 607 | } |
pbos@webrtc.org | b5e6bfc | 2014-09-12 11:05:55 | [diff] [blame] | 608 | |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 609 | TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp", |
danilchap | e5b4141 | 2016-08-22 10:39:23 | [diff] [blame] | 610 | rtp_timestamp); |
Sergey Ulanov | 525df3f | 2016-08-03 00:46:41 | [diff] [blame] | 611 | return true; |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 612 | } |
| 613 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 | [diff] [blame] | 614 | uint32_t RTPSenderVideo::VideoBitrateSent() const { |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 615 | rtc::CritScope cs(&stats_crit_); |
| 616 | return video_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0); |
stefan@webrtc.org | fbea4e5 | 2011-10-27 16:08:29 | [diff] [blame] | 617 | } |
| 618 | |
pbos@webrtc.org | 2f44673 | 2013-04-08 11:08:41 | [diff] [blame] | 619 | uint32_t RTPSenderVideo::FecOverheadRate() const { |
sprang | cd349d9 | 2016-07-13 16:11:28 | [diff] [blame] | 620 | rtc::CritScope cs(&stats_crit_); |
| 621 | return fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0); |
stefan@webrtc.org | d0bdab0 | 2011-10-14 14:24:54 | [diff] [blame] | 622 | } |
| 623 | |
stefan@webrtc.org | 6a4bef4 | 2011-12-22 12:52:41 | [diff] [blame] | 624 | int RTPSenderVideo::SelectiveRetransmissions() const { |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 625 | rtc::CritScope cs(&crit_); |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 626 | return retransmission_settings_; |
stefan@webrtc.org | 6a4bef4 | 2011-12-22 12:52:41 | [diff] [blame] | 627 | } |
| 628 | |
mflodman | fcf54bd | 2015-04-14 19:28:08 | [diff] [blame] | 629 | void RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) { |
danilchap | 7c9426c | 2016-04-14 10:05:31 | [diff] [blame] | 630 | rtc::CritScope cs(&crit_); |
Sergey Ulanov | ec4f068 | 2016-07-28 22:19:10 | [diff] [blame] | 631 | retransmission_settings_ = settings; |
stefan@webrtc.org | 6a4bef4 | 2011-12-22 12:52:41 | [diff] [blame] | 632 | } |
| 633 | |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 634 | StorageType RTPSenderVideo::GetStorageType( |
| 635 | uint8_t temporal_id, |
| 636 | int32_t retransmission_settings, |
| 637 | int64_t expected_retransmission_time_ms) { |
| 638 | if (retransmission_settings == kRetransmitOff) |
| 639 | return StorageType::kDontRetransmit; |
| 640 | if (retransmission_settings == kRetransmitAllPackets) |
| 641 | return StorageType::kAllowRetransmission; |
| 642 | |
| 643 | rtc::CritScope cs(&stats_crit_); |
| 644 | // Media packet storage. |
| 645 | if ((retransmission_settings & kConditionallyRetransmitHigherLayers) && |
| 646 | UpdateConditionalRetransmit(temporal_id, |
| 647 | expected_retransmission_time_ms)) { |
| 648 | retransmission_settings |= kRetransmitHigherLayers; |
| 649 | } |
| 650 | |
| 651 | if (temporal_id == kNoTemporalIdx) |
| 652 | return kAllowRetransmission; |
| 653 | |
| 654 | if ((retransmission_settings & kRetransmitBaseLayer) && temporal_id == 0) |
| 655 | return kAllowRetransmission; |
| 656 | |
| 657 | if ((retransmission_settings & kRetransmitHigherLayers) && temporal_id > 0) |
| 658 | return kAllowRetransmission; |
| 659 | |
| 660 | return kDontRetransmit; |
| 661 | } |
| 662 | |
| 663 | uint8_t RTPSenderVideo::GetTemporalId(const RTPVideoHeader& header) { |
Philip Eliasson | d52a1a6 | 2018-09-07 13:03:55 | [diff] [blame] | 664 | struct TemporalIdGetter { |
| 665 | uint8_t operator()(const RTPVideoHeaderVP8& vp8) { return vp8.temporalIdx; } |
| 666 | uint8_t operator()(const RTPVideoHeaderVP9& vp9) { |
| 667 | return vp9.temporal_idx; |
| 668 | } |
| 669 | uint8_t operator()(const RTPVideoHeaderH264&) { return kNoTemporalIdx; } |
| 670 | uint8_t operator()(const absl::monostate&) { return kNoTemporalIdx; } |
| 671 | }; |
| 672 | return absl::visit(TemporalIdGetter(), header.video_type_header); |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 673 | } |
| 674 | |
| 675 | bool RTPSenderVideo::UpdateConditionalRetransmit( |
| 676 | uint8_t temporal_id, |
| 677 | int64_t expected_retransmission_time_ms) { |
| 678 | int64_t now_ms = clock_->TimeInMilliseconds(); |
| 679 | // Update stats for any temporal layer. |
| 680 | TemporalLayerStats* current_layer_stats = |
| 681 | &frame_stats_by_temporal_layer_[temporal_id]; |
| 682 | current_layer_stats->frame_rate_fp1000s.Update(1, now_ms); |
| 683 | int64_t tl_frame_interval = now_ms - current_layer_stats->last_frame_time_ms; |
| 684 | current_layer_stats->last_frame_time_ms = now_ms; |
| 685 | |
| 686 | // Conditional retransmit only applies to upper layers. |
| 687 | if (temporal_id != kNoTemporalIdx && temporal_id > 0) { |
| 688 | if (tl_frame_interval >= kMaxUnretransmittableFrameIntervalMs) { |
| 689 | // Too long since a retransmittable frame in this layer, enable NACK |
| 690 | // protection. |
| 691 | return true; |
| 692 | } else { |
| 693 | // Estimate when the next frame of any lower layer will be sent. |
| 694 | const int64_t kUndefined = std::numeric_limits<int64_t>::max(); |
| 695 | int64_t expected_next_frame_time = kUndefined; |
| 696 | for (int i = temporal_id - 1; i >= 0; --i) { |
| 697 | TemporalLayerStats* stats = &frame_stats_by_temporal_layer_[i]; |
Danil Chapovalov | d264df5 | 2018-06-14 10:59:38 | [diff] [blame] | 698 | absl::optional<uint32_t> rate = stats->frame_rate_fp1000s.Rate(now_ms); |
sprang | a8ae6f2 | 2017-09-04 14:23:56 | [diff] [blame] | 699 | if (rate) { |
| 700 | int64_t tl_next = stats->last_frame_time_ms + 1000000 / *rate; |
| 701 | if (tl_next - now_ms > -expected_retransmission_time_ms && |
| 702 | tl_next < expected_next_frame_time) { |
| 703 | expected_next_frame_time = tl_next; |
| 704 | } |
| 705 | } |
| 706 | } |
| 707 | |
| 708 | if (expected_next_frame_time == kUndefined || |
| 709 | expected_next_frame_time - now_ms > expected_retransmission_time_ms) { |
| 710 | // The next frame in a lower layer is expected at a later time (or |
| 711 | // unable to tell due to lack of data) than a retransmission is |
| 712 | // estimated to be able to arrive, so allow this packet to be nacked. |
| 713 | return true; |
| 714 | } |
| 715 | } |
| 716 | } |
| 717 | |
| 718 | return false; |
| 719 | } |
| 720 | |
pbos@webrtc.org | d900e8b | 2013-07-03 15:12:26 | [diff] [blame] | 721 | } // namespace webrtc |