rtp_format_h264.cc (11294B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 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 11 #include "modules/rtp_rtcp/source/rtp_format_h264.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <cstring> 16 #include <vector> 17 18 #include "absl/algorithm/container.h" 19 #include "api/array_view.h" 20 #include "common_video/h264/h264_common.h" 21 #include "modules/rtp_rtcp/source/byte_io.h" 22 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" 23 #include "modules/video_coding/codecs/h264/include/h264_globals.h" 24 #include "rtc_base/checks.h" 25 #include "rtc_base/logging.h" 26 27 namespace webrtc { 28 namespace { 29 30 constexpr size_t kNalHeaderSize = 1; 31 constexpr size_t kFuAHeaderSize = 2; 32 constexpr size_t kLengthFieldSize = 2; 33 34 } // namespace 35 36 RtpPacketizerH264::RtpPacketizerH264(ArrayView<const uint8_t> payload, 37 PayloadSizeLimits limits, 38 H264PacketizationMode packetization_mode) 39 : limits_(limits), num_packets_left_(0) { 40 // Guard against uninitialized memory in packetization_mode. 41 RTC_CHECK(packetization_mode == H264PacketizationMode::NonInterleaved || 42 packetization_mode == H264PacketizationMode::SingleNalUnit); 43 44 for (const auto& nalu : H264::FindNaluIndices(payload)) { 45 input_fragments_.push_back( 46 payload.subview(nalu.payload_start_offset, nalu.payload_size)); 47 } 48 bool has_empty_fragments = absl::c_any_of( 49 input_fragments_, 50 [](const ArrayView<const uint8_t> fragment) { return fragment.empty(); }); 51 if (has_empty_fragments || !GeneratePackets(packetization_mode)) { 52 // If empty fragments were found or we failed to generate all the packets, 53 // discard already generated packets in case the caller would ignore the 54 // return value and still try to call NextPacket(). 55 num_packets_left_ = 0; 56 while (!packets_.empty()) { 57 packets_.pop(); 58 } 59 } 60 } 61 62 RtpPacketizerH264::~RtpPacketizerH264() = default; 63 64 size_t RtpPacketizerH264::NumPackets() const { 65 return num_packets_left_; 66 } 67 68 bool RtpPacketizerH264::GeneratePackets( 69 H264PacketizationMode packetization_mode) { 70 for (size_t i = 0; i < input_fragments_.size();) { 71 RTC_DCHECK(!input_fragments_[i].empty()); 72 switch (packetization_mode) { 73 case H264PacketizationMode::SingleNalUnit: 74 if (!PacketizeSingleNalu(i)) 75 return false; 76 ++i; 77 break; 78 case H264PacketizationMode::NonInterleaved: 79 int fragment_len = input_fragments_[i].size(); 80 int single_packet_capacity = limits_.max_payload_len; 81 if (input_fragments_.size() == 1) 82 single_packet_capacity -= limits_.single_packet_reduction_len; 83 else if (i == 0) 84 single_packet_capacity -= limits_.first_packet_reduction_len; 85 else if (i + 1 == input_fragments_.size()) 86 single_packet_capacity -= limits_.last_packet_reduction_len; 87 88 if (fragment_len > single_packet_capacity) { 89 if (!PacketizeFuA(i)) 90 return false; 91 ++i; 92 } else { 93 i = PacketizeStapA(i); 94 } 95 break; 96 } 97 } 98 return true; 99 } 100 101 bool RtpPacketizerH264::PacketizeFuA(size_t fragment_index) { 102 // Fragment payload into packets (FU-A). 103 ArrayView<const uint8_t> fragment = input_fragments_[fragment_index]; 104 105 PayloadSizeLimits limits = limits_; 106 // Leave room for the FU-A header. 107 limits.max_payload_len -= kFuAHeaderSize; 108 // Update single/first/last packet reductions unless it is single/first/last 109 // fragment. 110 if (input_fragments_.size() != 1) { 111 // if this fragment is put into a single packet, it might still be the 112 // first or the last packet in the whole sequence of packets. 113 if (fragment_index == input_fragments_.size() - 1) { 114 limits.single_packet_reduction_len = limits_.last_packet_reduction_len; 115 } else if (fragment_index == 0) { 116 limits.single_packet_reduction_len = limits_.first_packet_reduction_len; 117 } else { 118 limits.single_packet_reduction_len = 0; 119 } 120 } 121 if (fragment_index != 0) 122 limits.first_packet_reduction_len = 0; 123 if (fragment_index != input_fragments_.size() - 1) 124 limits.last_packet_reduction_len = 0; 125 126 // Strip out the original header. 127 size_t payload_left = fragment.size() - kNalHeaderSize; 128 int offset = kNalHeaderSize; 129 130 std::vector<int> payload_sizes = SplitAboutEqually(payload_left, limits); 131 if (payload_sizes.empty()) 132 return false; 133 134 for (size_t i = 0; i < payload_sizes.size(); ++i) { 135 int packet_length = payload_sizes[i]; 136 RTC_CHECK_GT(packet_length, 0); 137 packets_.push(PacketUnit(fragment.subview(offset, packet_length), 138 /*first_fragment=*/i == 0, 139 /*last_fragment=*/i == payload_sizes.size() - 1, 140 false, fragment[0])); 141 offset += packet_length; 142 payload_left -= packet_length; 143 } 144 num_packets_left_ += payload_sizes.size(); 145 RTC_CHECK_EQ(0, payload_left); 146 return true; 147 } 148 149 size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { 150 // Aggregate fragments into one packet (STAP-A). 151 size_t payload_size_left = limits_.max_payload_len; 152 int aggregated_fragments = 0; 153 size_t fragment_headers_length = 0; 154 ArrayView<const uint8_t> fragment = input_fragments_[fragment_index]; 155 RTC_CHECK_GE(payload_size_left, fragment.size()); 156 ++num_packets_left_; 157 158 const bool has_first_fragment = fragment_index == 0; 159 auto payload_size_needed = [&] { 160 size_t fragment_size = fragment.size() + fragment_headers_length; 161 bool has_last_fragment = fragment_index == input_fragments_.size() - 1; 162 if (has_first_fragment && has_last_fragment) { 163 return fragment_size + limits_.single_packet_reduction_len; 164 } else if (has_first_fragment) { 165 return fragment_size + limits_.first_packet_reduction_len; 166 } else if (has_last_fragment) { 167 return fragment_size + limits_.last_packet_reduction_len; 168 } else { 169 return fragment_size; 170 } 171 }; 172 while (payload_size_left >= payload_size_needed()) { 173 RTC_CHECK_GT(fragment.size(), 0); 174 175 packets_.push(PacketUnit(fragment, /*first=*/aggregated_fragments == 0, 176 /*last=*/false, /*aggregated=*/true, fragment[0])); 177 payload_size_left -= fragment.size(); 178 payload_size_left -= fragment_headers_length; 179 180 fragment_headers_length = kLengthFieldSize; 181 // If we are going to try to aggregate more fragments into this packet 182 // we need to add the STAP-A NALU header and a length field for the first 183 // NALU of this packet. 184 if (aggregated_fragments == 0) 185 fragment_headers_length += kNalHeaderSize + kLengthFieldSize; 186 ++aggregated_fragments; 187 188 // Next fragment. 189 ++fragment_index; 190 if (fragment_index == input_fragments_.size()) 191 break; 192 fragment = input_fragments_[fragment_index]; 193 } 194 RTC_CHECK_GT(aggregated_fragments, 0); 195 packets_.back().last_fragment = true; 196 return fragment_index; 197 } 198 199 bool RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) { 200 // Add a single NALU to the queue, no aggregation. 201 size_t payload_size_left = limits_.max_payload_len; 202 if (input_fragments_.size() == 1) 203 payload_size_left -= limits_.single_packet_reduction_len; 204 else if (fragment_index == 0) 205 payload_size_left -= limits_.first_packet_reduction_len; 206 else if (fragment_index + 1 == input_fragments_.size()) 207 payload_size_left -= limits_.last_packet_reduction_len; 208 ArrayView<const uint8_t> fragment = input_fragments_[fragment_index]; 209 if (payload_size_left < fragment.size()) { 210 RTC_LOG(LS_ERROR) << "Failed to fit a fragment to packet in SingleNalu " 211 "packetization mode. Payload size left " 212 << payload_size_left << ", fragment length " 213 << fragment.size() << ", packet capacity " 214 << limits_.max_payload_len; 215 return false; 216 } 217 RTC_CHECK(!fragment.empty()); 218 packets_.push(PacketUnit(fragment, /*first=*/true, /*last=*/true, 219 /*aggregated=*/false, fragment[0])); 220 ++num_packets_left_; 221 return true; 222 } 223 224 bool RtpPacketizerH264::NextPacket(RtpPacketToSend* rtp_packet) { 225 RTC_DCHECK(rtp_packet); 226 if (packets_.empty()) { 227 return false; 228 } 229 230 PacketUnit packet = packets_.front(); 231 if (packet.first_fragment && packet.last_fragment) { 232 // Single NAL unit packet. 233 rtp_packet->SetPayload(packet.source_fragment); 234 packets_.pop(); 235 input_fragments_.pop_front(); 236 } else if (packet.aggregated) { 237 NextAggregatePacket(rtp_packet); 238 } else { 239 NextFragmentPacket(rtp_packet); 240 } 241 rtp_packet->SetMarker(packets_.empty()); 242 --num_packets_left_; 243 return true; 244 } 245 246 void RtpPacketizerH264::NextAggregatePacket(RtpPacketToSend* rtp_packet) { 247 // Reserve maximum available payload, set actual payload size later. 248 size_t payload_capacity = rtp_packet->FreeCapacity(); 249 RTC_CHECK_GE(payload_capacity, kNalHeaderSize); 250 uint8_t* buffer = rtp_packet->AllocatePayload(payload_capacity); 251 RTC_DCHECK(buffer); 252 PacketUnit* packet = &packets_.front(); 253 RTC_CHECK(packet->first_fragment); 254 // STAP-A NALU header. 255 buffer[0] = 256 (packet->header & (kH264FBit | kH264NriMask)) | H264::NaluType::kStapA; 257 size_t index = kNalHeaderSize; 258 bool is_last_fragment = packet->last_fragment; 259 while (packet->aggregated) { 260 ArrayView<const uint8_t> fragment = packet->source_fragment; 261 RTC_CHECK_LE(index + kLengthFieldSize + fragment.size(), payload_capacity); 262 // Add NAL unit length field. 263 ByteWriter<uint16_t>::WriteBigEndian(&buffer[index], fragment.size()); 264 index += kLengthFieldSize; 265 // Add NAL unit. 266 memcpy(&buffer[index], fragment.data(), fragment.size()); 267 index += fragment.size(); 268 packets_.pop(); 269 input_fragments_.pop_front(); 270 if (is_last_fragment) 271 break; 272 packet = &packets_.front(); 273 is_last_fragment = packet->last_fragment; 274 } 275 RTC_CHECK(is_last_fragment); 276 rtp_packet->SetPayloadSize(index); 277 } 278 279 void RtpPacketizerH264::NextFragmentPacket(RtpPacketToSend* rtp_packet) { 280 PacketUnit* packet = &packets_.front(); 281 // NAL unit fragmented over multiple packets (FU-A). 282 // We do not send original NALU header, so it will be replaced by the 283 // FU indicator header of the first packet. 284 uint8_t fu_indicator = 285 (packet->header & (kH264FBit | kH264NriMask)) | H264::NaluType::kFuA; 286 uint8_t fu_header = 0; 287 288 // S | E | R | 5 bit type. 289 fu_header |= (packet->first_fragment ? kH264SBit : 0); 290 fu_header |= (packet->last_fragment ? kH264EBit : 0); 291 uint8_t type = packet->header & kH264TypeMask; 292 fu_header |= type; 293 ArrayView<const uint8_t> fragment = packet->source_fragment; 294 uint8_t* buffer = 295 rtp_packet->AllocatePayload(kFuAHeaderSize + fragment.size()); 296 buffer[0] = fu_indicator; 297 buffer[1] = fu_header; 298 memcpy(buffer + kFuAHeaderSize, fragment.data(), fragment.size()); 299 if (packet->last_fragment) 300 input_fragments_.pop_front(); 301 packets_.pop(); 302 } 303 304 } // namespace webrtc