diff options
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/h264_sps_pps_tracker.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_coding/h264_sps_pps_tracker.cc | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/h264_sps_pps_tracker.cc b/third_party/libwebrtc/modules/video_coding/h264_sps_pps_tracker.cc new file mode 100644 index 0000000000..5a7eae7b42 --- /dev/null +++ b/third_party/libwebrtc/modules/video_coding/h264_sps_pps_tracker.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/h264_sps_pps_tracker.h" + +#include <memory> +#include <string> +#include <utility> + +#include "absl/types/variant.h" +#include "common_video/h264/h264_common.h" +#include "common_video/h264/pps_parser.h" +#include "common_video/h264/sps_parser.h" +#include "modules/video_coding/codecs/h264/include/h264_globals.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace video_coding { + +namespace { +const uint8_t start_code_h264[] = {0, 0, 0, 1}; +} // namespace + +H264SpsPpsTracker::H264SpsPpsTracker() = default; +H264SpsPpsTracker::~H264SpsPpsTracker() = default; + +H264SpsPpsTracker::PpsInfo::PpsInfo() = default; +H264SpsPpsTracker::PpsInfo::PpsInfo(PpsInfo&& rhs) = default; +H264SpsPpsTracker::PpsInfo& H264SpsPpsTracker::PpsInfo::operator=( + PpsInfo&& rhs) = default; +H264SpsPpsTracker::PpsInfo::~PpsInfo() = default; + +H264SpsPpsTracker::SpsInfo::SpsInfo() = default; +H264SpsPpsTracker::SpsInfo::SpsInfo(SpsInfo&& rhs) = default; +H264SpsPpsTracker::SpsInfo& H264SpsPpsTracker::SpsInfo::operator=( + SpsInfo&& rhs) = default; +H264SpsPpsTracker::SpsInfo::~SpsInfo() = default; + +H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream( + rtc::ArrayView<const uint8_t> bitstream, + RTPVideoHeader* video_header) { + RTC_DCHECK(video_header); + RTC_DCHECK(video_header->codec == kVideoCodecH264); + RTC_DCHECK_GT(bitstream.size(), 0); + + auto& h264_header = + absl::get<RTPVideoHeaderH264>(video_header->video_type_header); + + bool append_sps_pps = false; + auto sps = sps_data_.end(); + auto pps = pps_data_.end(); + + for (size_t i = 0; i < h264_header.nalus_length; ++i) { + const NaluInfo& nalu = h264_header.nalus[i]; + switch (nalu.type) { + case H264::NaluType::kSps: { + SpsInfo& sps_info = sps_data_[nalu.sps_id]; + sps_info.width = video_header->width; + sps_info.height = video_header->height; + break; + } + case H264::NaluType::kPps: { + pps_data_[nalu.pps_id].sps_id = nalu.sps_id; + break; + } + case H264::NaluType::kIdr: { + // If this is the first packet of an IDR, make sure we have the required + // SPS/PPS and also calculate how much extra space we need in the buffer + // to prepend the SPS/PPS to the bitstream with start codes. + if (video_header->is_first_packet_in_frame) { + if (nalu.pps_id == -1) { + RTC_LOG(LS_WARNING) << "No PPS id in IDR nalu."; + return {kRequestKeyframe}; + } + + pps = pps_data_.find(nalu.pps_id); + if (pps == pps_data_.end()) { + RTC_LOG(LS_WARNING) + << "No PPS with id << " << nalu.pps_id << " received"; + return {kRequestKeyframe}; + } + + sps = sps_data_.find(pps->second.sps_id); + if (sps == sps_data_.end()) { + RTC_LOG(LS_WARNING) + << "No SPS with id << " << pps->second.sps_id << " received"; + return {kRequestKeyframe}; + } + + // Since the first packet of every keyframe should have its width and + // height set we set it here in the case of it being supplied out of + // band. + video_header->width = sps->second.width; + video_header->height = sps->second.height; + + // If the SPS/PPS was supplied out of band then we will have saved + // the actual bitstream in `data`. + if (sps->second.data && pps->second.data) { + RTC_DCHECK_GT(sps->second.size, 0); + RTC_DCHECK_GT(pps->second.size, 0); + append_sps_pps = true; + } + } + break; + } + default: + break; + } + } + + RTC_CHECK(!append_sps_pps || + (sps != sps_data_.end() && pps != pps_data_.end())); + + // Calculate how much space we need for the rest of the bitstream. + size_t required_size = 0; + + if (append_sps_pps) { + required_size += sps->second.size + sizeof(start_code_h264); + required_size += pps->second.size + sizeof(start_code_h264); + } + + if (h264_header.packetization_type == kH264StapA) { + const uint8_t* nalu_ptr = bitstream.data() + 1; + while (nalu_ptr < bitstream.data() + bitstream.size() - 1) { + required_size += sizeof(start_code_h264); + + // The first two bytes describe the length of a segment. + uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1]; + nalu_ptr += 2; + + required_size += segment_length; + nalu_ptr += segment_length; + } + } else { + if (h264_header.nalus_length > 0) { + required_size += sizeof(start_code_h264); + } + required_size += bitstream.size(); + } + + // Then we copy to the new buffer. + H264SpsPpsTracker::FixedBitstream fixed; + fixed.bitstream.EnsureCapacity(required_size); + + if (append_sps_pps) { + // Insert SPS. + fixed.bitstream.AppendData(start_code_h264); + fixed.bitstream.AppendData(sps->second.data.get(), sps->second.size); + + // Insert PPS. + fixed.bitstream.AppendData(start_code_h264); + fixed.bitstream.AppendData(pps->second.data.get(), pps->second.size); + + // Update codec header to reflect the newly added SPS and PPS. + NaluInfo sps_info; + sps_info.type = H264::NaluType::kSps; + sps_info.sps_id = sps->first; + sps_info.pps_id = -1; + NaluInfo pps_info; + pps_info.type = H264::NaluType::kPps; + pps_info.sps_id = sps->first; + pps_info.pps_id = pps->first; + if (h264_header.nalus_length + 2 <= kMaxNalusPerPacket) { + h264_header.nalus[h264_header.nalus_length++] = sps_info; + h264_header.nalus[h264_header.nalus_length++] = pps_info; + } else { + RTC_LOG(LS_WARNING) << "Not enough space in H.264 codec header to insert " + "SPS/PPS provided out-of-band."; + } + } + + // Copy the rest of the bitstream and insert start codes. + if (h264_header.packetization_type == kH264StapA) { + const uint8_t* nalu_ptr = bitstream.data() + 1; + while (nalu_ptr < bitstream.data() + bitstream.size() - 1) { + fixed.bitstream.AppendData(start_code_h264); + + // The first two bytes describe the length of a segment. + uint16_t segment_length = nalu_ptr[0] << 8 | nalu_ptr[1]; + nalu_ptr += 2; + + size_t copy_end = nalu_ptr - bitstream.data() + segment_length; + if (copy_end > bitstream.size()) { + return {kDrop}; + } + + fixed.bitstream.AppendData(nalu_ptr, segment_length); + nalu_ptr += segment_length; + } + } else { + if (h264_header.nalus_length > 0) { + fixed.bitstream.AppendData(start_code_h264); + } + fixed.bitstream.AppendData(bitstream.data(), bitstream.size()); + } + + fixed.action = kInsert; + return fixed; +} + +void H264SpsPpsTracker::InsertSpsPpsNalus(const std::vector<uint8_t>& sps, + const std::vector<uint8_t>& pps) { + constexpr size_t kNaluHeaderOffset = 1; + if (sps.size() < kNaluHeaderOffset) { + RTC_LOG(LS_WARNING) << "SPS size " << sps.size() << " is smaller than " + << kNaluHeaderOffset; + return; + } + if ((sps[0] & 0x1f) != H264::NaluType::kSps) { + RTC_LOG(LS_WARNING) << "SPS Nalu header missing"; + return; + } + if (pps.size() < kNaluHeaderOffset) { + RTC_LOG(LS_WARNING) << "PPS size " << pps.size() << " is smaller than " + << kNaluHeaderOffset; + return; + } + if ((pps[0] & 0x1f) != H264::NaluType::kPps) { + RTC_LOG(LS_WARNING) << "SPS Nalu header missing"; + return; + } + absl::optional<SpsParser::SpsState> parsed_sps = SpsParser::ParseSps( + sps.data() + kNaluHeaderOffset, sps.size() - kNaluHeaderOffset); + absl::optional<PpsParser::PpsState> parsed_pps = PpsParser::ParsePps( + pps.data() + kNaluHeaderOffset, pps.size() - kNaluHeaderOffset); + + if (!parsed_sps) { + RTC_LOG(LS_WARNING) << "Failed to parse SPS."; + } + + if (!parsed_pps) { + RTC_LOG(LS_WARNING) << "Failed to parse PPS."; + } + + if (!parsed_pps || !parsed_sps) { + return; + } + + SpsInfo sps_info; + sps_info.size = sps.size(); + sps_info.width = parsed_sps->width; + sps_info.height = parsed_sps->height; + uint8_t* sps_data = new uint8_t[sps_info.size]; + memcpy(sps_data, sps.data(), sps_info.size); + sps_info.data.reset(sps_data); + sps_data_[parsed_sps->id] = std::move(sps_info); + + PpsInfo pps_info; + pps_info.size = pps.size(); + pps_info.sps_id = parsed_pps->sps_id; + uint8_t* pps_data = new uint8_t[pps_info.size]; + memcpy(pps_data, pps.data(), pps_info.size); + pps_info.data.reset(pps_data); + pps_data_[parsed_pps->id] = std::move(pps_info); + + RTC_LOG(LS_INFO) << "Inserted SPS id " << parsed_sps->id << " and PPS id " + << parsed_pps->id << " (referencing SPS " + << parsed_pps->sps_id << ")"; +} + +} // namespace video_coding +} // namespace webrtc |