h264_sps_pps_tracker.cc (8037B)
1 /* 2 * Copyright (c) 2016 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/video_coding/h264_sps_pps_tracker.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <optional> 16 #include <utility> 17 #include <vector> 18 19 #include "api/array_view.h" 20 #include "api/video/video_codec_type.h" 21 #include "common_video/h264/h264_common.h" 22 #include "common_video/h264/pps_parser.h" 23 #include "common_video/h264/sps_parser.h" 24 #include "modules/rtp_rtcp/source/rtp_video_header.h" 25 #include "modules/video_coding/codecs/h264/include/h264_globals.h" 26 #include "rtc_base/byte_buffer.h" 27 #include "rtc_base/checks.h" 28 #include "rtc_base/logging.h" 29 30 namespace webrtc { 31 namespace video_coding { 32 33 namespace { 34 const uint8_t start_code_h264[] = {0, 0, 0, 1}; 35 } // namespace 36 37 H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream( 38 ArrayView<const uint8_t> bitstream, 39 RTPVideoHeader* video_header) { 40 RTC_DCHECK(video_header); 41 RTC_DCHECK(video_header->codec == kVideoCodecH264); 42 RTC_DCHECK_GT(bitstream.size(), 0); 43 44 auto& h264_header = 45 std::get<RTPVideoHeaderH264>(video_header->video_type_header); 46 47 bool append_sps_pps = false; 48 auto sps = sps_data_.end(); 49 auto pps = pps_data_.end(); 50 51 for (const NaluInfo& nalu : h264_header.nalus) { 52 switch (nalu.type) { 53 case H264::NaluType::kSps: { 54 SpsInfo& sps_info = sps_data_[nalu.sps_id]; 55 sps_info.width = video_header->width; 56 sps_info.height = video_header->height; 57 break; 58 } 59 case H264::NaluType::kPps: { 60 pps_data_[nalu.pps_id].sps_id = nalu.sps_id; 61 break; 62 } 63 case H264::NaluType::kIdr: { 64 // If this is the first packet of an IDR, make sure we have the required 65 // SPS/PPS and also calculate how much extra space we need in the buffer 66 // to prepend the SPS/PPS to the bitstream with start codes. 67 if (video_header->is_first_packet_in_frame) { 68 if (nalu.pps_id == -1) { 69 RTC_LOG(LS_WARNING) << "No PPS id in IDR nalu."; 70 return {.action = kRequestKeyframe}; 71 } 72 73 pps = pps_data_.find(nalu.pps_id); 74 if (pps == pps_data_.end()) { 75 RTC_LOG(LS_WARNING) 76 << "No PPS with id << " << nalu.pps_id << " received"; 77 return {.action = kRequestKeyframe}; 78 } 79 80 sps = sps_data_.find(pps->second.sps_id); 81 if (sps == sps_data_.end()) { 82 RTC_LOG(LS_WARNING) 83 << "No SPS with id << " << pps->second.sps_id << " received"; 84 return {.action = kRequestKeyframe}; 85 } 86 87 // Since the first packet of every keyframe should have its width and 88 // height set we set it here in the case of it being supplied out of 89 // band. 90 video_header->width = sps->second.width; 91 video_header->height = sps->second.height; 92 93 // If the SPS/PPS was supplied out of band then we will have saved 94 // the actual bitstream in `data`. 95 if (!sps->second.data.empty() && !pps->second.data.empty()) { 96 append_sps_pps = true; 97 } 98 } 99 break; 100 } 101 default: 102 break; 103 } 104 } 105 106 RTC_CHECK(!append_sps_pps || 107 (sps != sps_data_.end() && pps != pps_data_.end())); 108 109 // Calculate how much space we need for the rest of the bitstream. 110 size_t required_size = 0; 111 112 if (append_sps_pps) { 113 required_size += sps->second.data.size() + sizeof(start_code_h264); 114 required_size += pps->second.data.size() + sizeof(start_code_h264); 115 } 116 117 if (h264_header.packetization_type == kH264StapA) { 118 ByteBufferReader nalu(bitstream.subview(1)); 119 while (nalu.Length() > 0) { 120 required_size += sizeof(start_code_h264); 121 122 // The first two bytes describe the length of a segment. 123 uint16_t segment_length; 124 if (!nalu.ReadUInt16(&segment_length)) 125 return {.action = kDrop}; 126 if (segment_length == 0 || segment_length > nalu.Length()) { 127 return {.action = kDrop}; 128 } 129 required_size += segment_length; 130 nalu.Consume(segment_length); 131 } 132 } else { 133 if (!h264_header.nalus.empty()) { 134 required_size += sizeof(start_code_h264); 135 } 136 required_size += bitstream.size(); 137 } 138 139 // Then we copy to the new buffer. 140 H264SpsPpsTracker::FixedBitstream fixed; 141 fixed.bitstream.EnsureCapacity(required_size); 142 143 if (append_sps_pps) { 144 // Insert SPS. 145 fixed.bitstream.AppendData(start_code_h264); 146 fixed.bitstream.AppendData(sps->second.data); 147 148 // Insert PPS. 149 fixed.bitstream.AppendData(start_code_h264); 150 fixed.bitstream.AppendData(pps->second.data); 151 152 // Update codec header to reflect the newly added SPS and PPS. 153 h264_header.nalus.push_back( 154 {.type = H264::NaluType::kSps, .sps_id = sps->first, .pps_id = -1}); 155 h264_header.nalus.push_back({.type = H264::NaluType::kPps, 156 .sps_id = sps->first, 157 .pps_id = pps->first}); 158 } 159 160 // Copy the rest of the bitstream and insert start codes. 161 if (h264_header.packetization_type == kH264StapA) { 162 ByteBufferReader nalu(bitstream.subview(1)); 163 while (nalu.Length() > 0) { 164 fixed.bitstream.AppendData(start_code_h264); 165 166 // The first two bytes describe the length of a segment. 167 uint16_t segment_length; 168 if (!nalu.ReadUInt16(&segment_length)) 169 return {.action = kDrop}; 170 if (segment_length == 0 || segment_length > nalu.Length()) { 171 return {.action = kDrop}; 172 } 173 fixed.bitstream.AppendData(nalu.Data(), segment_length); 174 nalu.Consume(segment_length); 175 } 176 } else { 177 if (!h264_header.nalus.empty()) { 178 fixed.bitstream.AppendData(start_code_h264); 179 } 180 fixed.bitstream.AppendData(bitstream.data(), bitstream.size()); 181 } 182 183 fixed.action = kInsert; 184 return fixed; 185 } 186 187 void H264SpsPpsTracker::InsertSpsPpsNalus(const std::vector<uint8_t>& sps, 188 const std::vector<uint8_t>& pps) { 189 constexpr size_t kNaluHeaderOffset = 1; 190 if (sps.size() < kNaluHeaderOffset) { 191 RTC_LOG(LS_WARNING) << "SPS size " << sps.size() << " is smaller than " 192 << kNaluHeaderOffset; 193 return; 194 } 195 if ((sps[0] & 0x1f) != H264::NaluType::kSps) { 196 RTC_LOG(LS_WARNING) << "SPS Nalu header missing"; 197 return; 198 } 199 if (pps.size() < kNaluHeaderOffset) { 200 RTC_LOG(LS_WARNING) << "PPS size " << pps.size() << " is smaller than " 201 << kNaluHeaderOffset; 202 return; 203 } 204 if ((pps[0] & 0x1f) != H264::NaluType::kPps) { 205 RTC_LOG(LS_WARNING) << "SPS Nalu header missing"; 206 return; 207 } 208 std::optional<SpsParser::SpsState> parsed_sps = SpsParser::ParseSps( 209 ArrayView<const uint8_t>(sps).subview(kNaluHeaderOffset)); 210 std::optional<PpsParser::PpsState> parsed_pps = PpsParser::ParsePps( 211 ArrayView<const uint8_t>(pps).subview(kNaluHeaderOffset)); 212 213 if (!parsed_sps) { 214 RTC_LOG(LS_WARNING) << "Failed to parse SPS."; 215 } 216 217 if (!parsed_pps) { 218 RTC_LOG(LS_WARNING) << "Failed to parse PPS."; 219 } 220 221 if (!parsed_pps || !parsed_sps) { 222 return; 223 } 224 225 SpsInfo sps_info; 226 sps_info.width = parsed_sps->width; 227 sps_info.height = parsed_sps->height; 228 sps_info.data.SetData(sps); 229 sps_data_[parsed_sps->id] = std::move(sps_info); 230 231 PpsInfo pps_info; 232 pps_info.sps_id = parsed_pps->sps_id; 233 pps_info.data.SetData(pps); 234 pps_data_[parsed_pps->id] = std::move(pps_info); 235 236 RTC_LOG(LS_INFO) << "Inserted SPS id " << parsed_sps->id << " and PPS id " 237 << parsed_pps->id << " (referencing SPS " 238 << parsed_pps->sps_id << ")"; 239 } 240 241 } // namespace video_coding 242 } // namespace webrtc