red_payload_splitter.cc (7230B)
1 /* 2 * Copyright (c) 2012 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/audio_coding/neteq/red_payload_splitter.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <list> 16 #include <utility> 17 #include <vector> 18 19 #include "modules/audio_coding/neteq/decoder_database.h" 20 #include "modules/audio_coding/neteq/packet.h" 21 #include "rtc_base/buffer.h" 22 #include "rtc_base/checks.h" 23 #include "rtc_base/logging.h" 24 #include "rtc_base/numerics/safe_conversions.h" 25 26 namespace webrtc { 27 28 // The method loops through a list of packets {A, B, C, ...}. Each packet is 29 // split into its corresponding RED payloads, {A1, A2, ...}, which is 30 // temporarily held in the list `new_packets`. 31 // When the first packet in `packet_list` has been processed, the original 32 // packet is replaced by the new ones in `new_packets`, so that `packet_list` 33 // becomes: {A1, A2, ..., B, C, ...}. The method then continues with B, and C, 34 // until all the original packets have been replaced by their split payloads. 35 bool RedPayloadSplitter::SplitRed(PacketList* packet_list) { 36 // Too many RED blocks indicates that something is wrong. Clamp it at some 37 // reasonable value. 38 const size_t kMaxRedBlocks = 32; 39 bool ret = true; 40 PacketList::iterator it = packet_list->begin(); 41 while (it != packet_list->end()) { 42 Packet& red_packet = *it; 43 RTC_DCHECK(!red_packet.payload.empty()); 44 const uint8_t* payload_ptr = red_packet.payload.data(); 45 size_t payload_length = red_packet.payload.size(); 46 47 // Read RED headers (according to RFC 2198): 48 // 49 // 0 1 2 3 50 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 51 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 // |F| block PT | timestamp offset | block length | 53 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 // Last RED header: 55 // 0 1 2 3 4 5 6 7 56 // +-+-+-+-+-+-+-+-+ 57 // |0| Block PT | 58 // +-+-+-+-+-+-+-+-+ 59 60 struct RedHeader { 61 uint8_t payload_type; 62 uint32_t timestamp; 63 size_t payload_length; 64 }; 65 66 std::vector<RedHeader> new_headers; 67 bool last_block = false; 68 size_t sum_length = 0; 69 while (!last_block) { 70 if (payload_length == 0) { 71 RTC_LOG(LS_WARNING) << "SplitRed header too short"; 72 return false; 73 } 74 RedHeader new_header; 75 // Check the F bit. If F == 0, this was the last block. 76 last_block = ((*payload_ptr & 0x80) == 0); 77 // Bits 1 through 7 are payload type. 78 new_header.payload_type = payload_ptr[0] & 0x7F; 79 if (last_block) { 80 // No more header data to read. 81 sum_length += kRedLastHeaderLength; // Account for RED header size. 82 new_header.timestamp = red_packet.timestamp; 83 new_header.payload_length = red_packet.payload.size() - sum_length; 84 payload_ptr += kRedLastHeaderLength; // Advance to first payload byte. 85 payload_length -= kRedLastHeaderLength; 86 } else { 87 if (payload_length < kRedHeaderLength) { 88 RTC_LOG(LS_WARNING) << "SplitRed header too short"; 89 return false; 90 } 91 // Bits 8 through 21 are timestamp offset. 92 int timestamp_offset = 93 (payload_ptr[1] << 6) + ((payload_ptr[2] & 0xFC) >> 2); 94 new_header.timestamp = red_packet.timestamp - timestamp_offset; 95 // Bits 22 through 31 are payload length. 96 new_header.payload_length = 97 ((payload_ptr[2] & 0x03) << 8) + payload_ptr[3]; 98 99 sum_length += new_header.payload_length; 100 sum_length += kRedHeaderLength; // Account for RED header size. 101 102 payload_ptr += kRedHeaderLength; // Advance to next RED header. 103 payload_length -= kRedHeaderLength; 104 } 105 // Store in new list of packets. 106 if (new_header.payload_length > 0) { 107 new_headers.push_back(new_header); 108 } 109 } 110 111 if (new_headers.size() <= kMaxRedBlocks) { 112 // Populate the new packets with payload data. 113 // `payload_ptr` now points at the first payload byte. 114 PacketList new_packets; // An empty list to store the split packets in. 115 for (size_t i = 0; i != new_headers.size(); ++i) { 116 const auto& new_header = new_headers[i]; 117 size_t block_length = new_header.payload_length; 118 if (payload_ptr + block_length > 119 red_packet.payload.data() + red_packet.payload.size()) { 120 // The block lengths in the RED headers do not match the overall 121 // packet length. Something is corrupt. Discard this and the remaining 122 // payloads from this packet. 123 RTC_LOG(LS_WARNING) << "SplitRed length mismatch"; 124 ret = false; 125 break; 126 } 127 128 Packet new_packet; 129 new_packet.timestamp = new_header.timestamp; 130 new_packet.payload_type = new_header.payload_type; 131 new_packet.sequence_number = red_packet.sequence_number; 132 new_packet.priority.red_level = 133 dchecked_cast<int>((new_headers.size() - 1) - i); 134 new_packet.payload.SetData(payload_ptr, block_length); 135 new_packets.push_front(std::move(new_packet)); 136 payload_ptr += block_length; 137 } 138 // Insert new packets into original list, before the element pointed to by 139 // iterator `it`. 140 packet_list->splice(it, std::move(new_packets)); 141 } else { 142 RTC_LOG(LS_WARNING) << "SplitRed too many blocks: " << new_headers.size(); 143 ret = false; 144 } 145 // Remove `it` from the packet list. This operation effectively moves the 146 // iterator `it` to the next packet in the list. Thus, we do not have to 147 // increment it manually. 148 it = packet_list->erase(it); 149 } 150 return ret; 151 } 152 153 void RedPayloadSplitter::CheckRedPayloads( 154 PacketList* packet_list, 155 const DecoderDatabase& decoder_database) { 156 int main_payload_type = -1; 157 for (auto it = packet_list->begin(); it != packet_list->end(); /* */) { 158 uint8_t this_payload_type = it->payload_type; 159 if (decoder_database.IsRed(this_payload_type)) { 160 it = packet_list->erase(it); 161 continue; 162 } 163 if (!decoder_database.IsDtmf(this_payload_type) && 164 !decoder_database.IsComfortNoise(this_payload_type)) { 165 if (main_payload_type == -1) { 166 // This is the first packet in the list which is non-DTMF non-CNG. 167 main_payload_type = this_payload_type; 168 } else { 169 if (this_payload_type != main_payload_type) { 170 // We do not allow redundant payloads of a different type. 171 // Remove `it` from the packet list. This operation effectively 172 // moves the iterator `it` to the next packet in the list. Thus, we 173 // do not have to increment it manually. 174 it = packet_list->erase(it); 175 continue; 176 } 177 } 178 } 179 ++it; 180 } 181 } 182 183 } // namespace webrtc