using ::testing::ElementsAreArray;
using ::testing::Le;
using ::testing::SizeIs;
+using RtpPacketView = VideoRtpDepacketizer::RtpPacketView;
constexpr uint8_t kNewCodedVideoSequenceBit = 0b00'00'1000;
}
Av1Frame ReassembleFrame(rtc::ArrayView<const RtpPayload> rtp_payloads) {
- std::vector<rtc::ArrayView<const uint8_t>> payloads(rtp_payloads.size());
- for (size_t i = 0; i < rtp_payloads.size(); ++i) {
- payloads[i] = rtp_payloads[i];
+ std::vector<RtpPacketView> payloads;
+ for (const auto& payload : rtp_payloads) {
+ payloads.emplace_back(nullptr, payload);
}
return Av1Frame(VideoRtpDepacketizerAv1().AssembleFrame(payloads));
}
namespace webrtc {
rtc::scoped_refptr<EncodedImageBuffer> VideoRtpDepacketizer::AssembleFrame(
- rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads) {
+ rtc::ArrayView<RtpPacketView> rtp_payloads) {
size_t frame_size = 0;
- for (rtc::ArrayView<const uint8_t> payload : rtp_payloads) {
- frame_size += payload.size();
+ for (const auto& packet : rtp_payloads) {
+ frame_size += packet.payload.size();
}
rtc::scoped_refptr<EncodedImageBuffer> bitstream =
EncodedImageBuffer::Create(frame_size);
uint8_t* write_at = bitstream->data();
- for (rtc::ArrayView<const uint8_t> payload : rtp_payloads) {
- memcpy(write_at, payload.data(), payload.size());
- write_at += payload.size();
+ for (const auto& packet : rtp_payloads) {
+ memcpy(write_at, packet.payload.data(), packet.payload.size());
+ write_at += packet.payload.size();
}
RTC_DCHECK_EQ(write_at - bitstream->data(), bitstream->size());
return bitstream;
}
+void VideoRtpDepacketizer::SetCodecParams(
+ const std::map<std::string, std::string>& codec_params) {}
+
+VideoRtpDepacketizer::PacketAction VideoRtpDepacketizer::GetPacketAction(
+ rtc::ArrayView<const uint8_t> bitstream,
+ RTPVideoHeader* video_header) {
+ return kInsert;
+}
+
+void VideoRtpDepacketizer::AppendData(uint8_t** buf,
+ const uint8_t* data,
+ size_t size) {
+ memcpy(*buf, data, size);
+ *buf += size;
+}
+
} // namespace webrtc
#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H_
#define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H_
-#include <stdint.h>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
#include "absl/types/optional.h"
#include "api/array_view.h"
rtc::CopyOnWriteBuffer video_payload;
};
+ struct RtpPacketView {
+ const RTPVideoHeader* video_header;
+ const rtc::ArrayView<const uint8_t> payload;
+
+ RtpPacketView(const RTPVideoHeader* header,
+ const rtc::ArrayView<const uint8_t> rtp_payload)
+ : video_header(header), payload(rtp_payload) {}
+ };
+
+ enum PacketAction { kInsert, kDrop, kRequestKeyframe };
+
virtual ~VideoRtpDepacketizer() = default;
virtual absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) = 0;
virtual rtc::scoped_refptr<EncodedImageBuffer> AssembleFrame(
- rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads);
+ rtc::ArrayView<RtpPacketView> rtp_payloads);
+ virtual void SetCodecParams(
+ const std::map<std::string, std::string>& codec_params);
+ virtual PacketAction GetPacketAction(rtc::ArrayView<const uint8_t> bitstream,
+ RTPVideoHeader* video_header);
+
+ protected:
+ template <typename T, size_t N>
+ std::enable_if_t<sizeof(T) == 1> AppendData(uint8_t** buf,
+ const T (&array)[N]) {
+ AppendData(buf, reinterpret_cast<const uint8_t*>(array), N);
+ }
+
+ void AppendData(uint8_t** buf, const uint8_t* data, size_t size);
};
} // namespace webrtc
// fills ObuInfo::data field.
// Returns empty vector on error.
VectorObuInfo ParseObus(
- rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads) {
+ rtc::ArrayView<VideoRtpDepacketizer::RtpPacketView> rtp_payloads) {
VectorObuInfo obu_infos;
bool expect_continues_obu = false;
- for (rtc::ArrayView<const uint8_t> rtp_payload : rtp_payloads) {
+ for (const auto& rtp_packet : rtp_payloads) {
+ rtc::ArrayView<const uint8_t> rtp_payload = rtp_packet.payload;
rtc::ByteBufferReader payload(
reinterpret_cast<const char*>(rtp_payload.data()), rtp_payload.size());
uint8_t aggregation_header;
} // namespace
rtc::scoped_refptr<EncodedImageBuffer> VideoRtpDepacketizerAv1::AssembleFrame(
- rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads) {
+ rtc::ArrayView<RtpPacketView> rtp_payloads) {
VectorObuInfo obu_infos = ParseObus(rtp_payloads);
if (obu_infos.empty()) {
return nullptr;
~VideoRtpDepacketizerAv1() override = default;
rtc::scoped_refptr<EncodedImageBuffer> AssembleFrame(
- rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads)
- override;
+ rtc::ArrayView<RtpPacketView> rtp_payloads) override;
absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) override;
namespace {
using ::testing::ElementsAre;
+using RtpPacketView = VideoRtpDepacketizer::RtpPacketView;
// Signals number of the OBU (fragments) in the packet.
constexpr uint8_t kObuCountOne = 0b00'01'0000;
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'000, // / Frame
20, 30, 40}; // \ OBU
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
20,
30,
40}; // \ obu_payload
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
0b0'0110'100, // / Frame
0b010'01'000, // | extension_header
20, 30, 40}; // \ OBU
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
20,
30,
40}; // \ obu_payload
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'000, // / Frame
20}; // \ OBU
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
10, // \ OBU
0b0'0110'000, // / Frame
20}; // \ OBU
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
0b0'0110'000, 20, 30};
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
40};
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
30}; //
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
40}; //
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
70, 80, 90}; // \ tail of the frame OBU
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
const uint8_t payload4[] = {0b10'01'0000, // aggregation header
18};
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3,
- payload4};
+ RtpPacketView payloads[] = {{nullptr, payload1},
+ {nullptr, payload2},
+ {nullptr, payload3},
+ {nullptr, payload4}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
32};
const uint8_t payload4[] = {0b10'01'0000, // aggregation header
33, 34, 35, 36};
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3,
- payload4};
+ RtpPacketView payloads[] = {{nullptr, payload1},
+ {nullptr, payload2},
+ {nullptr, payload3},
+ {nullptr, payload4}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
payload1[2] = 0x01; // in two bytes
payload1[3] = 0b0'0110'000; // obu_header with size and extension bits unset.
payload1[4 + 42] = 0x42;
- rtc::ArrayView<const uint8_t> payloads[] = {payload1};
+ RtpPacketView payloads[] = {{nullptr, payload1}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_EQ(frame->size(), 2 + 127u);
payload2[1] = 96; // leb128 encoded size of 96 bytes in one byte
payload2[2 + 20] = 0x20;
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_EQ(frame->size(), 3 + 128u);
AssembleFrameFromAlmostEmptyPacketStartingAnOBU) {
const uint8_t payload1[] = {0b01'01'0000};
const uint8_t payload2[] = {0b10'01'0000, 0b0'0110'000, 10, 20, 30};
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
AssembleFrameFromAlmostEmptyPacketFinishingAnOBU) {
const uint8_t payload1[] = {0b01'01'0000, 0b0'0110'000, 10, 20, 30};
const uint8_t payload2[] = {0b10'01'0000};
- rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
+ RtpPacketView payloads[] = {{nullptr, payload1}, {nullptr, payload2}};
auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads);
ASSERT_TRUE(frame);
#include <cstddef>
#include <cstdint>
+#include <map>
+#include <string>
#include <utility>
#include <vector>
#include "common_video/h264/pps_parser.h"
#include "common_video/h264/sps_parser.h"
#include "common_video/h264/sps_vui_rewriter.h"
+#include "media/base/media_constants.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
+#include "modules/video_coding/codecs/h264/include/h264_globals.h"
+#include "modules/video_coding/h264_sprop_parameter_sets.h"
#include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
+const uint8_t start_code_h264[] = {0, 0, 0, 1};
+
constexpr size_t kNalHeaderSize = 1;
constexpr size_t kFuAHeaderSize = 2;
constexpr size_t kLengthFieldSize = 2;
"unit with original type: "
<< static_cast<int>(nalu.type);
}
- uint8_t original_nal_header = fnri | original_nal_type;
- rtp_payload =
- rtp_payload.Slice(kNalHeaderSize, rtp_payload.size() - kNalHeaderSize);
- rtp_payload.MutableData()[0] = original_nal_header;
- parsed_payload->video_payload = std::move(rtp_payload);
- } else {
- parsed_payload->video_payload =
- rtp_payload.Slice(kFuAHeaderSize, rtp_payload.size() - kFuAHeaderSize);
}
+ parsed_payload->video_payload =
+ rtp_payload.Slice(kFuAHeaderSize, rtp_payload.size() - kFuAHeaderSize);
+
if (original_nal_type == H264::NaluType::kIdr) {
parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameKey;
} else {
if (first_fragment) {
h264_header.nalus[h264_header.nalus_length] = nalu;
h264_header.nalus_length = 1;
+ h264_header.original_nal_header = fnri | original_nal_type;
}
return parsed_payload;
}
}
}
+void VideoRtpDepacketizerH264::SetCodecParams(
+ const std::map<std::string, std::string>& codec_params) {
+ webrtc::H264SpropParameterSets sprop_decoder;
+ auto sprop_base64_it =
+ codec_params.find(cricket::kH264FmtpSpropParameterSets);
+
+ if (sprop_base64_it == codec_params.end())
+ return;
+
+ if (!sprop_decoder.DecodeSprop(sprop_base64_it->second.c_str()))
+ return;
+
+ const std::vector<uint8_t>& sps = sprop_decoder.sps_nalu();
+ const std::vector<uint8_t>& pps = sprop_decoder.pps_nalu();
+ 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;
+ }
+
+ H264SpsInfo sps_info;
+ sps_info.size = sps.size();
+ sps_info.width = parsed_sps->width;
+ sps_info.height = parsed_sps->height;
+ sps_info.data.resize(sps_info.size);
+ memcpy(sps_info.data.data(), sps.data(), sps_info.size);
+ sps_data_[parsed_sps->id] = std::move(sps_info);
+
+ H264PpsInfo pps_info;
+ pps_info.size = pps.size();
+ pps_info.sps_id = parsed_pps->sps_id;
+ pps_info.data.resize(pps_info.size);
+ memcpy(pps_info.data.data(), pps.data(), pps_info.size);
+ 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 << ")";
+}
+
+VideoRtpDepacketizer::PacketAction VideoRtpDepacketizerH264::GetPacketAction(
+ 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: {
+ H264SpsInfo& 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.empty() && !pps->second.data.empty()) {
+ 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()));
+ if (append_sps_pps) {
+ h264_header.extra_data =
+ absl::make_optional(std::make_tuple(pps->second, sps->second));
+
+ // 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.";
+ }
+ }
+
+ if (h264_header.packetization_type == kH264StapA) {
+ const uint8_t* nalu_ptr = bitstream.data() + 1;
+ while (nalu_ptr < bitstream.data() + bitstream.size() - 1) {
+ // 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;
+ }
+
+ nalu_ptr += segment_length;
+ }
+ }
+
+ return kInsert;
+}
+
+rtc::scoped_refptr<EncodedImageBuffer> VideoRtpDepacketizerH264::AssembleFrame(
+ rtc::ArrayView<RtpPacketView> rtp_payloads) {
+ size_t frame_size = 0;
+ for (const auto& packet : rtp_payloads) {
+ frame_size += CalculatePacketSize(packet);
+ }
+
+ rtc::scoped_refptr<EncodedImageBuffer> bitstream =
+ EncodedImageBuffer::Create(frame_size);
+
+ uint8_t* buf = bitstream->data();
+ for (const auto& packet : rtp_payloads) {
+ AppendPacket(&buf, packet);
+ }
+ RTC_DCHECK_EQ(buf - bitstream->data(), bitstream->size());
+ return bitstream;
+}
+
+size_t VideoRtpDepacketizerH264::CalculatePacketSize(
+ const RtpPacketView& packet) {
+ const auto& h264_header =
+ absl::get<RTPVideoHeaderH264>(packet.video_header->video_type_header);
+
+ size_t required_size = 0;
+
+ if (h264_header.extra_data) {
+ required_size += sizeof(start_code_h264);
+ required_size += std::get<H264SpsInfo>(*h264_header.extra_data).size;
+ required_size += sizeof(start_code_h264);
+ required_size += std::get<H264PpsInfo>(*h264_header.extra_data).size;
+ }
+
+ if (h264_header.packetization_type == kH264StapA) {
+ const uint8_t* nalu_ptr = packet.payload.data() + 1;
+ while (nalu_ptr < packet.payload.data() + packet.payload.size() - 1) {
+ RTC_DCHECK(packet.video_header->is_first_packet_in_frame);
+ 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);
+ }
+ if (h264_header.nalus_length > 0 &&
+ h264_header.packetization_type == kH264FuA) {
+ required_size += 1;
+ }
+ required_size += packet.payload.size();
+ }
+
+ return required_size;
+}
+
+void VideoRtpDepacketizerH264::AppendPacket(uint8_t** buf,
+ const RtpPacketView& packet) {
+ const auto& h264_header =
+ absl::get<RTPVideoHeaderH264>(packet.video_header->video_type_header);
+ if (h264_header.extra_data) {
+ // Insert SPS.
+ const auto& sps = std::get<H264SpsInfo>(*h264_header.extra_data);
+ AppendData(buf, start_code_h264);
+ AppendData(buf, sps.data.data(), sps.size);
+
+ // Insert PPS.
+ const auto& pps = std::get<H264PpsInfo>(*h264_header.extra_data);
+ AppendData(buf, start_code_h264);
+ AppendData(buf, pps.data.data(), pps.size);
+ }
+
+ // Copy the rest of the bitstream and insert start codes.
+ if (h264_header.packetization_type == kH264StapA) {
+ const uint8_t* nalu_ptr = packet.payload.data() + 1;
+ while (nalu_ptr < packet.payload.data() + packet.payload.size() - 1) {
+ AppendData(buf, 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 - packet.payload.data() + segment_length;
+ RTC_DCHECK_LE(copy_end, packet.payload.size());
+
+ AppendData(buf, nalu_ptr, segment_length);
+ nalu_ptr += segment_length;
+ }
+ } else {
+ if (h264_header.nalus_length > 0) {
+ AppendData(buf, start_code_h264);
+ if (h264_header.packetization_type == kH264FuA) {
+ AppendData(buf, &h264_header.original_nal_header, 1);
+ }
+ }
+ AppendData(buf, packet.payload.data(), packet.payload.size());
+ }
+}
+
} // namespace webrtc
#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
#define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
+#include <map>
+#include <string>
+
#include "absl/types/optional.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/copy_on_write_buffer.h"
absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) override;
+
+ rtc::scoped_refptr<EncodedImageBuffer> AssembleFrame(
+ rtc::ArrayView<RtpPacketView> rtp_packets) override;
+
+ void SetCodecParams(
+ const std::map<std::string, std::string>& codec_params) override;
+ PacketAction GetPacketAction(rtc::ArrayView<const uint8_t> bitstream,
+ RTPVideoHeader* video_header) override;
+
+ private:
+ size_t CalculatePacketSize(const RtpPacketView& packet);
+ void AppendPacket(uint8_t** buf, const RtpPacketView& packet);
+
+ std::map<uint32_t, H264PpsInfo> pps_data_;
+ std::map<uint32_t, H264SpsInfo> sps_data_;
};
} // namespace webrtc
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "common_video/h264/h264_common.h"
-#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
+#include "media/base/media_constants.h"
#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/third_party/base64/base64.h"
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::IsEmpty;
using ::testing::SizeIs;
+using RtpPacketView = VideoRtpDepacketizer::RtpPacketView;
+
enum Nalu {
kSlice = 1,
kIdr = 5,
constexpr uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04};
constexpr uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11};
+const uint8_t start_code[] = {0, 0, 0, 1};
+
+rtc::ArrayView<const uint8_t> Bitstream(const EncodedImageBuffer& fixed) {
+ return fixed;
+}
+
+void ExpectSpsPpsIdr(const RTPVideoHeaderH264& codec_header,
+ uint8_t sps_id,
+ uint8_t pps_id) {
+ bool contains_sps = false;
+ bool contains_pps = false;
+ bool contains_idr = false;
+ for (const auto& nalu : codec_header.nalus) {
+ if (nalu.type == H264::NaluType::kSps) {
+ EXPECT_EQ(sps_id, nalu.sps_id);
+ contains_sps = true;
+ } else if (nalu.type == H264::NaluType::kPps) {
+ EXPECT_EQ(sps_id, nalu.sps_id);
+ EXPECT_EQ(pps_id, nalu.pps_id);
+ contains_pps = true;
+ } else if (nalu.type == H264::NaluType::kIdr) {
+ EXPECT_EQ(pps_id, nalu.pps_id);
+ contains_idr = true;
+ }
+ }
+ EXPECT_TRUE(contains_sps);
+ EXPECT_TRUE(contains_pps);
+ EXPECT_TRUE(contains_idr);
+}
+
+class H264VideoHeader : public RTPVideoHeader {
+ public:
+ H264VideoHeader() {
+ codec = kVideoCodecH264;
+ is_first_packet_in_frame = false;
+ auto& h264_header = video_type_header.emplace<RTPVideoHeaderH264>();
+ h264_header.nalus_length = 0;
+ h264_header.packetization_type = kH264SingleNalu;
+ }
+
+ RTPVideoHeaderH264& h264() {
+ return absl::get<RTPVideoHeaderH264>(video_type_header);
+ }
+};
+
TEST(VideoRtpDepacketizerH264Test, SingleNalu) {
uint8_t packet[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5 (IDR).
rtc::CopyOnWriteBuffer rtp_payload(packet);
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed1 =
depacketizer.Parse(rtc::CopyOnWriteBuffer(packet1));
ASSERT_TRUE(parsed1);
- // We expect that the first packet is one byte shorter since the FU-A header
- // has been replaced by the original nal header.
- EXPECT_THAT(rtc::MakeArrayView(parsed1->video_payload.cdata(),
- parsed1->video_payload.size()),
- ElementsAreArray(kExpected1));
+ std::vector<uint8_t> parsed1_data;
+ // We expect that for the first packet is returned video header carries
+ // the original nal header extracted from FU-A header and payload will
+ // be 2 bytes shorter.
+ parsed1_data.push_back(
+ absl::get<RTPVideoHeaderH264>(parsed1->video_header.video_type_header)
+ .original_nal_header);
+ parsed1_data.insert(
+ parsed1_data.end(), parsed1->video_payload.cdata(),
+ parsed1->video_payload.cdata() + parsed1->video_payload.size());
+ EXPECT_THAT(parsed1_data, ElementsAreArray(kExpected1));
EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame);
EXPECT_EQ(h264.nalus[0].pps_id, -1);
}
+class TestH264SpsPpsTracker : public ::testing::Test {
+ public:
+ void AddSps(H264VideoHeader* header,
+ uint8_t sps_id,
+ std::vector<uint8_t>* data) {
+ NaluInfo info;
+ info.type = H264::NaluType::kSps;
+ info.sps_id = sps_id;
+ info.pps_id = -1;
+ data->push_back(H264::NaluType::kSps);
+ data->push_back(sps_id); // The sps data, just a single byte.
+
+ header->h264().nalus[header->h264().nalus_length++] = info;
+ }
+
+ void AddPps(H264VideoHeader* header,
+ uint8_t sps_id,
+ uint8_t pps_id,
+ std::vector<uint8_t>* data) {
+ NaluInfo info;
+ info.type = H264::NaluType::kPps;
+ info.sps_id = sps_id;
+ info.pps_id = pps_id;
+ data->push_back(H264::NaluType::kPps);
+ data->push_back(pps_id); // The pps data, just a single byte.
+
+ header->h264().nalus[header->h264().nalus_length++] = info;
+ }
+
+ void AddIdr(H264VideoHeader* header, int pps_id) {
+ NaluInfo info;
+ info.type = H264::NaluType::kIdr;
+ info.sps_id = -1;
+ info.pps_id = pps_id;
+
+ header->h264().nalus[header->h264().nalus_length++] = info;
+ }
+
+ void SetCodecParams(const std::vector<uint8_t>& sps,
+ const std::vector<uint8_t>& pps) {
+ std::string sps_str;
+ rtc::Base64::EncodeFromArray(sps.data(), sps.size(), &sps_str);
+ std::string pps_str;
+ rtc::Base64::EncodeFromArray(pps.data(), pps.size(), &pps_str);
+ std::map<std::string, std::string> codec_params = {
+ {cricket::kH264FmtpSpropParameterSets, sps_str + "," + pps_str}};
+ tracker_.SetCodecParams(codec_params);
+ }
+
+ protected:
+ VideoRtpDepacketizerH264 tracker_;
+};
+
+TEST_F(TestH264SpsPpsTracker, NoNalus) {
+ uint8_t data[] = {1, 2, 3};
+ H264VideoHeader header;
+ header.h264().packetization_type = kH264FuA;
+
+ auto action = tracker_.GetPacketAction(data, &header);
+ RtpPacketView packets[] = {{&header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+
+ EXPECT_EQ(action, VideoRtpDepacketizer::kInsert);
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(data));
+}
+
+TEST_F(TestH264SpsPpsTracker, FuAFirstPacket) {
+ uint8_t data[] = {1, 2, 3};
+ H264VideoHeader header;
+ header.h264().packetization_type = kH264FuA;
+ header.h264().nalus_length = 1;
+ header.h264().original_nal_header = kIdr;
+ header.is_first_packet_in_frame = true;
+
+ auto action = tracker_.GetPacketAction(data, &header);
+ RtpPacketView packets[] = {{&header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+
+ EXPECT_EQ(action, VideoRtpDepacketizer::kInsert);
+ std::vector<uint8_t> expected;
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {kIdr, 1, 2, 3});
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(expected));
+}
+
+TEST_F(TestH264SpsPpsTracker, StapAIncorrectSegmentLength) {
+ uint8_t data[] = {0, 0, 2, 0};
+ H264VideoHeader header;
+ header.h264().packetization_type = kH264StapA;
+ header.is_first_packet_in_frame = true;
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &header),
+ VideoRtpDepacketizer::kDrop);
+}
+
+TEST_F(TestH264SpsPpsTracker, SingleNaluInsertStartCode) {
+ uint8_t data[] = {1, 2, 3};
+ H264VideoHeader header;
+ header.h264().nalus_length = 1;
+
+ auto action = tracker_.GetPacketAction(data, &header);
+ RtpPacketView packets[] = {{&header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+
+ EXPECT_EQ(action, VideoRtpDepacketizer::kInsert);
+ std::vector<uint8_t> expected;
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {1, 2, 3});
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(expected));
+}
+
+TEST_F(TestH264SpsPpsTracker, NoStartCodeInsertedForSubsequentFuAPacket) {
+ std::vector<uint8_t> data = {1, 2, 3};
+ H264VideoHeader header;
+ header.h264().packetization_type = kH264FuA;
+ // Since no NALU begin in this packet the nalus_length is zero.
+ header.h264().nalus_length = 0;
+
+ auto action = tracker_.GetPacketAction(data, &header);
+ RtpPacketView packets[] = {{&header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+
+ EXPECT_EQ(action, VideoRtpDepacketizer::kInsert);
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(data));
+}
+
+TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsPpsInserted) {
+ std::vector<uint8_t> data = {1, 2, 3};
+ H264VideoHeader header;
+ header.is_first_packet_in_frame = true;
+ AddIdr(&header, 0);
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &header),
+ VideoRtpDepacketizer::kRequestKeyframe);
+}
+
+TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoPpsInserted) {
+ std::vector<uint8_t> data = {1, 2, 3};
+ H264VideoHeader header;
+ header.is_first_packet_in_frame = true;
+ AddSps(&header, 0, &data);
+ AddIdr(&header, 0);
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &header),
+ VideoRtpDepacketizer::kRequestKeyframe);
+}
+
+TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsInserted) {
+ std::vector<uint8_t> data = {1, 2, 3};
+ H264VideoHeader header;
+ header.is_first_packet_in_frame = true;
+ AddPps(&header, 0, 0, &data);
+ AddIdr(&header, 0);
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &header),
+ VideoRtpDepacketizer::kRequestKeyframe);
+}
+
+TEST_F(TestH264SpsPpsTracker, SpsPpsPacketThenIdrFirstPacket) {
+ std::vector<uint8_t> data;
+ H264VideoHeader sps_pps_header;
+ // Insert SPS/PPS
+ AddSps(&sps_pps_header, 0, &data);
+ AddPps(&sps_pps_header, 0, 1, &data);
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &sps_pps_header),
+ VideoRtpDepacketizer::kInsert);
+
+ // Insert first packet of the IDR
+ H264VideoHeader idr_header;
+ idr_header.is_first_packet_in_frame = true;
+ AddIdr(&idr_header, 1);
+ data = {1, 2, 3};
+
+ auto action = tracker_.GetPacketAction(data, &idr_header);
+ RtpPacketView packets[] = {{&idr_header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+ EXPECT_EQ(action, VideoRtpDepacketizer::kInsert);
+
+ std::vector<uint8_t> expected;
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {1, 2, 3});
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(expected));
+}
+
+TEST_F(TestH264SpsPpsTracker, SpsPpsIdrInStapA) {
+ std::vector<uint8_t> data;
+ H264VideoHeader header;
+ header.h264().packetization_type = kH264StapA;
+ header.is_first_packet_in_frame = true; // Always true for StapA
+
+ data.insert(data.end(), {0}); // First byte is ignored
+ data.insert(data.end(), {0, 2}); // Length of segment
+ AddSps(&header, 13, &data);
+ data.insert(data.end(), {0, 2}); // Length of segment
+ AddPps(&header, 13, 27, &data);
+ data.insert(data.end(), {0, 5}); // Length of segment
+ AddIdr(&header, 27);
+ data.insert(data.end(), {1, 2, 3, 2, 1});
+
+ auto action = tracker_.GetPacketAction(data, &header);
+ RtpPacketView packets[] = {{&header, data}};
+ auto frame = tracker_.AssembleFrame(packets);
+
+ EXPECT_THAT(action, VideoRtpDepacketizer::kInsert);
+
+ std::vector<uint8_t> expected;
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {H264::NaluType::kSps, 13});
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {H264::NaluType::kPps, 27});
+ expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
+ expected.insert(expected.end(), {1, 2, 3, 2, 1});
+ EXPECT_THAT(Bitstream(*frame), ElementsAreArray(expected));
+}
+
+TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBand) {
+ constexpr uint8_t kData[] = {1, 2, 3};
+
+ // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
+ // width: 320, height: 240
+ const std::vector<uint8_t> sps(
+ {0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00,
+ 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60});
+ const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
+ SetCodecParams(sps, pps);
+
+ // Insert first packet of the IDR.
+ H264VideoHeader idr_header;
+ idr_header.is_first_packet_in_frame = true;
+ AddIdr(&idr_header, 0);
+ EXPECT_EQ(idr_header.h264().nalus_length, 1u);
+
+ auto action = tracker_.GetPacketAction(kData, &idr_header);
+ EXPECT_THAT(action, VideoRtpDepacketizer::kInsert);
+
+ EXPECT_EQ(idr_header.h264().nalus_length, 3u);
+ EXPECT_EQ(idr_header.width, 320u);
+ EXPECT_EQ(idr_header.height, 240u);
+ ExpectSpsPpsIdr(idr_header.h264(), 0, 0);
+}
+
+TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandWrongNaluHeader) {
+ constexpr uint8_t kData[] = {1, 2, 3};
+
+ // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
+ // Nalu headers manupilated afterwards.
+ const std::vector<uint8_t> sps(
+ {0xff, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00,
+ 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60});
+ const std::vector<uint8_t> pps({0xff, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
+ SetCodecParams(sps, pps);
+
+ // Insert first packet of the IDR.
+ H264VideoHeader idr_header;
+ idr_header.is_first_packet_in_frame = true;
+ AddIdr(&idr_header, 0);
+
+ EXPECT_EQ(tracker_.GetPacketAction(kData, &idr_header),
+ VideoRtpDepacketizer::kRequestKeyframe);
+}
+
+TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandIncompleteNalu) {
+ constexpr uint8_t kData[] = {1, 2, 3};
+
+ // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
+ // Nalus damaged afterwards.
+ const std::vector<uint8_t> sps({0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9});
+ const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
+ SetCodecParams(sps, pps);
+
+ // Insert first packet of the IDR.
+ H264VideoHeader idr_header;
+ idr_header.is_first_packet_in_frame = true;
+ AddIdr(&idr_header, 0);
+
+ EXPECT_EQ(tracker_.GetPacketAction(kData, &idr_header),
+ VideoRtpDepacketizer::kRequestKeyframe);
+}
+
+TEST_F(TestH264SpsPpsTracker, SaveRestoreWidthHeight) {
+ std::vector<uint8_t> data;
+
+ // Insert an SPS/PPS packet with width/height and make sure
+ // that information is set on the first IDR packet.
+ H264VideoHeader sps_pps_header;
+ AddSps(&sps_pps_header, 0, &data);
+ AddPps(&sps_pps_header, 0, 1, &data);
+ sps_pps_header.width = 320;
+ sps_pps_header.height = 240;
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &sps_pps_header),
+ VideoRtpDepacketizer::kInsert);
+
+ H264VideoHeader idr_header;
+ idr_header.is_first_packet_in_frame = true;
+ AddIdr(&idr_header, 1);
+ data.insert(data.end(), {1, 2, 3});
+
+ EXPECT_EQ(tracker_.GetPacketAction(data, &idr_header),
+ VideoRtpDepacketizer::kInsert);
+
+ EXPECT_EQ(idr_header.width, 320);
+ EXPECT_EQ(idr_header.height, 240);
+}
+
} // namespace
} // namespace webrtc
#include <cstddef>
#include <cstdint>
+#include <map>
#include <memory>
+#include <string>
+#include <tuple>
#include <utility>
#include <vector>
-#include "absl/base/macros.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
-#include "common_video/h264/h264_common.h"
#include "common_video/h265/h265_common.h"
#include "common_video/h265/h265_pps_parser.h"
#include "common_video/h265/h265_sps_parser.h"
namespace webrtc {
namespace {
+const uint8_t start_code_h265[] = {0, 0, 0, 1};
+
enum NaluType {
kTrailN = 0,
kTrailR = 1,
nalu_start_offsets.push_back(0);
}
h265_header.nalu_type = nal_type;
+ h265_header.first_fragment = true;
parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameDelta;
nalu_start_offsets.push_back(rtp_payload.size() +
"unit with original type: "
<< static_cast<int>(nalu.type);
}
- rtp_payload = rtp_payload.Slice(1, rtp_payload.size() - 1);
- rtp_payload.MutableData()[0] = f | original_nal_type << 1 | layer_id_h;
- rtp_payload.MutableData()[1] = layer_id_l_unshifted | tid;
- parsed_payload->video_payload = std::move(rtp_payload);
- } else {
- parsed_payload->video_payload = rtp_payload.Slice(
- kHevcNalHeaderSize + kHevcFuHeaderSize,
- rtp_payload.size() - kHevcNalHeaderSize - kHevcFuHeaderSize);
}
+ parsed_payload->video_payload = rtp_payload.Slice(
+ kHevcNalHeaderSize + kHevcFuHeaderSize,
+ rtp_payload.size() - kHevcNalHeaderSize - kHevcFuHeaderSize);
if (original_nal_type == H265::NaluType::kIdrWRadl ||
original_nal_type == H265::NaluType::kIdrNLp ||
.emplace<RTPVideoHeaderH265>();
h265_header.packetization_type = kH265FU;
h265_header.nalu_type = original_nal_type;
+ h265_header.first_fragment = first_fragment;
if (first_fragment) {
h265_header.nalus[h265_header.nalus_length] = nalu;
h265_header.nalus_length = 1;
+ h265_header.fau_nal_header[0] = f | original_nal_type << 1 | layer_id_h;
+ h265_header.fau_nal_header[1] = layer_id_l_unshifted | tid;
}
return parsed_payload;
}
}
}
+void VideoRtpDepacketizerH265::SetCodecParams(
+ const std::map<std::string, std::string>& codec_params) {
+ RTC_LOG(LS_WARNING) << "VideoRtpDepacketizerH265 add parsing out-of-band "
+ << "codec parameters...";
+}
+
+VideoRtpDepacketizer::PacketAction VideoRtpDepacketizerH265::GetPacketAction(
+ rtc::ArrayView<const uint8_t> bitstream,
+ RTPVideoHeader* video_header) {
+ RTC_DCHECK(video_header);
+ RTC_DCHECK(video_header->codec == kVideoCodecH265);
+
+ auto& h265_header =
+ absl::get<RTPVideoHeaderH265>(video_header->video_type_header);
+
+ bool append_vps_sps_pps = false;
+ auto vps = vps_data_.end();
+ auto sps = sps_data_.end();
+ auto pps = pps_data_.end();
+
+ for (size_t i = 0; i < h265_header.nalus_length; ++i) {
+ const H265NaluInfo& nalu = h265_header.nalus[i];
+ switch (nalu.type) {
+ case H265::NaluType::kVps: {
+ vps_data_[nalu.vps_id].size = 0;
+ break;
+ }
+ case H265::NaluType::kSps: {
+ sps_data_[nalu.sps_id].vps_id = nalu.vps_id;
+ sps_data_[nalu.sps_id].width = video_header->width;
+ sps_data_[nalu.sps_id].height = video_header->height;
+ break;
+ }
+ case H265::NaluType::kPps: {
+ pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
+ break;
+ }
+ case H265::NaluType::kIdrWRadl:
+ case H265::NaluType::kIdrNLp:
+ case H265::NaluType::kCra: {
+ // 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;
+ }
+
+ vps = vps_data_.find(sps->second.vps_id);
+ if (vps == vps_data_.end()) {
+ RTC_LOG(LS_WARNING)
+ << "No VPS with id " << sps->second.vps_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 VPS/SPS/PPS was supplied out of band then we will have saved
+ // the actual bitstream in |data|.
+ // This branch is not verified.
+ if (!vps->second.data.empty() && !sps->second.data.empty() &&
+ !pps->second.data.empty()) {
+ RTC_DCHECK_GT(vps->second.size, 0);
+ RTC_DCHECK_GT(sps->second.size, 0);
+ RTC_DCHECK_GT(pps->second.size, 0);
+ append_vps_sps_pps = true;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ RTC_CHECK(!append_vps_sps_pps ||
+ (sps != sps_data_.end() && pps != pps_data_.end()));
+
+ if (append_vps_sps_pps) {
+ h265_header.extra_data = absl::make_optional(
+ std::make_tuple(vps->second, pps->second, sps->second));
+
+ // Update codec header to reflect the newly added SPS and PPS.
+ H265NaluInfo vps_info;
+ vps_info.type = H265::NaluType::kVps;
+ vps_info.vps_id = vps->first;
+ vps_info.sps_id = -1;
+ vps_info.pps_id = -1;
+ H265NaluInfo sps_info;
+ sps_info.type = H265::NaluType::kSps;
+ sps_info.vps_id = vps->first;
+ sps_info.sps_id = sps->first;
+ sps_info.pps_id = -1;
+ H265NaluInfo pps_info;
+ pps_info.type = H265::NaluType::kPps;
+ pps_info.vps_id = vps->first;
+ pps_info.sps_id = sps->first;
+ pps_info.pps_id = pps->first;
+ if (h265_header.nalus_length + 2 <= kMaxNalusPerPacket) {
+ h265_header.nalus[h265_header.nalus_length++] = vps_info;
+ h265_header.nalus[h265_header.nalus_length++] = sps_info;
+ h265_header.nalus[h265_header.nalus_length++] = pps_info;
+ } else {
+ RTC_LOG(LS_WARNING) << "Not enough space in H.265 codec header to insert "
+ "SPS/PPS provided out-of-band.";
+ }
+ }
+
+ if (h265_header.packetization_type == kH265AP) {
+ const uint8_t* nalu_ptr = bitstream.data() + 1;
+ while (nalu_ptr < bitstream.data() + bitstream.size()) {
+ // 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;
+ }
+
+ nalu_ptr += segment_length;
+ }
+ }
+
+ return kInsert;
+}
+
+rtc::scoped_refptr<EncodedImageBuffer> VideoRtpDepacketizerH265::AssembleFrame(
+ rtc::ArrayView<RtpPacketView> rtp_payloads) {
+ size_t frame_size = 0;
+ for (const auto& packet : rtp_payloads) {
+ frame_size += CalculatePacketSize(packet);
+ }
+
+ rtc::scoped_refptr<EncodedImageBuffer> bitstream =
+ EncodedImageBuffer::Create(frame_size);
+
+ uint8_t* buf = bitstream->data();
+ for (const auto& packet : rtp_payloads) {
+ AppendPacket(&buf, packet);
+ }
+ RTC_DCHECK_EQ(buf - bitstream->data(), bitstream->size());
+ return bitstream;
+}
+
+size_t VideoRtpDepacketizerH265::CalculatePacketSize(
+ const RtpPacketView& packet) {
+ auto& h265_header =
+ absl::get<RTPVideoHeaderH265>(packet.video_header->video_type_header);
+ size_t required_size = 0;
+
+ if (h265_header.extra_data) {
+ required_size += sizeof(start_code_h265);
+ required_size += std::get<H265VpsInfo>(*h265_header.extra_data).size;
+ required_size += sizeof(start_code_h265);
+ required_size += std::get<H265SpsInfo>(*h265_header.extra_data).size;
+ required_size += sizeof(start_code_h265);
+ required_size += std::get<H265PpsInfo>(*h265_header.extra_data).size;
+ }
+
+ if (h265_header.packetization_type == kH265AP) {
+ const uint8_t* nalu_ptr = packet.payload.data() + 1;
+ while (nalu_ptr < packet.payload.data() + packet.payload.size()) {
+ RTC_DCHECK(h265_header.first_fragment);
+ required_size += sizeof(start_code_h265);
+
+ // 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 {
+ // Note: In h.264 this is "h264_header.nalus_length > 0"
+ if (h265_header.first_fragment) {
+ required_size += sizeof(start_code_h265);
+ required_size += (h265_header.packetization_type == kH265FU ? 2 : 0);
+ }
+ required_size += packet.payload.size();
+ }
+
+ return required_size;
+}
+
+void VideoRtpDepacketizerH265::AppendPacket(uint8_t** buf,
+ const RtpPacketView& packet) {
+ auto& h265_header =
+ absl::get<RTPVideoHeaderH265>(packet.video_header->video_type_header);
+
+ if (h265_header.extra_data) {
+ // Insert VPS.
+ const auto& vps = std::get<H265VpsInfo>(*h265_header.extra_data);
+ AppendData(buf, start_code_h265);
+ AppendData(buf, vps.data.data(), vps.size);
+
+ // Insert SPS.
+ const auto& sps = std::get<H265SpsInfo>(*h265_header.extra_data);
+ AppendData(buf, start_code_h265);
+ AppendData(buf, sps.data.data(), sps.size);
+
+ // Insert PPS.
+ const auto& pps = std::get<H265PpsInfo>(*h265_header.extra_data);
+ AppendData(buf, start_code_h265);
+ AppendData(buf, pps.data.data(), pps.size);
+ }
+
+ // Copy the rest of the bitstream and insert start codes.
+ if (h265_header.packetization_type == kH265AP) {
+ const uint8_t* nalu_ptr = packet.payload.data() + 1;
+ while (nalu_ptr < packet.payload.data() + packet.payload.size()) {
+ AppendData(buf, start_code_h265);
+
+ // 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 - packet.payload.data() + segment_length;
+ RTC_DCHECK_LE(copy_end, packet.payload.size());
+
+ AppendData(buf, nalu_ptr, segment_length);
+ nalu_ptr += segment_length;
+ }
+ } else {
+ // For h.264 it is "h264_header.nalus_length > 0"
+ if (h265_header.first_fragment) {
+ AppendData(buf, start_code_h265);
+ if (h265_header.packetization_type == kH265FU) {
+ AppendData(buf, h265_header.fau_nal_header, 2);
+ }
+ }
+ AppendData(buf, packet.payload.data(), packet.payload.size());
+ }
+}
+
} // namespace webrtc
#ifndef THIRD_PARTY_WEBRTC_MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H265_H_
#define THIRD_PARTY_WEBRTC_MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H265_H_
+#include <map>
+#include <string>
+
#include "absl/types/optional.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/copy_on_write_buffer.h"
absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) override;
+
+ rtc::scoped_refptr<EncodedImageBuffer> AssembleFrame(
+ rtc::ArrayView<RtpPacketView> rtp_packets) override;
+
+ void SetCodecParams(
+ const std::map<std::string, std::string>& codec_params) override;
+ PacketAction GetPacketAction(rtc::ArrayView<const uint8_t> bitstream,
+ RTPVideoHeader* video_header) override;
+
+ private:
+ size_t CalculatePacketSize(const RtpPacketView& packet);
+ void AppendPacket(uint8_t** buf, const RtpPacketView& packet);
+
+ std::map<uint32_t, H265VpsInfo> vps_data_;
+ std::map<uint32_t, H265PpsInfo> pps_data_;
+ std::map<uint32_t, H265SpsInfo> sps_data_;
};
} // namespace webrtc
"generic_decoder.h",
"h264_sprop_parameter_sets.cc",
"h264_sprop_parameter_sets.h",
- "h264_sps_pps_tracker.cc",
- "h264_sps_pps_tracker.h",
"include/video_codec_initializer.h",
"inter_frame_delay.cc",
"inter_frame_delay.h",
"video_receiver2.h",
]
- if (tizen_product_tv && enable_platform_hevc) {
- sources += [
- "h265_vps_sps_pps_tracker.cc",
- "h265_vps_sps_pps_tracker.h",
- ]
- }
-
deps = [
":codec_globals_headers",
":encoded_frame",
"frame_dependencies_calculator_unittest.cc",
"generic_decoder_unittest.cc",
"h264_sprop_parameter_sets_unittest.cc",
- "h264_sps_pps_tracker_unittest.cc",
"histogram_unittest.cc",
"jitter_buffer_unittest.cc",
"jitter_estimator_tests.cc",
#define MODULES_VIDEO_CODING_CODECS_H264_INCLUDE_H264_GLOBALS_H_
#include <string>
+#include <tuple>
+#include <vector>
+#include "absl/types/optional.h"
#include "modules/video_coding/codecs/interface/common_constants.h"
#include "rtc_base/checks.h"
const size_t kMaxNalusPerPacket = 10;
+struct H264PpsInfo {
+ int sps_id = -1;
+ size_t size = 0;
+ std::vector<uint8_t> data; // TODO(a.bujalski) shared_ptr to array?
+};
+
+struct H264SpsInfo {
+ size_t size = 0;
+ int width = -1;
+ int height = -1;
+ std::vector<uint8_t> data;
+};
+
struct RTPVideoHeaderH264 {
// The NAL unit type. If this is a header for a
// fragmented packet, it's the NAL unit type of
// The packetization mode of this transport. Packetization mode
// determines which packetization types are allowed when packetizing.
H264PacketizationMode packetization_mode;
+ // valid only for first packet of kH264FuA
+ uint8_t original_nal_header;
+ absl::optional<std::tuple<H264PpsInfo, H264SpsInfo>> extra_data;
};
-
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_CODECS_H264_INCLUDE_H264_GLOBALS_H_
#ifndef THIRD_PARTY_WEBRTC_MODULES_VIDEO_CODING_CODECS_H265_INCLUDE_H265_GLOBALS_H_
#define THIRD_PARTY_WEBRTC_MODULES_VIDEO_CODING_CODECS_H265_INCLUDE_H265_GLOBALS_H_
+#include <tuple>
+#include <vector>
+
+#include "absl/types/optional.h"
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
namespace webrtc {
SingleNalUnit // Mode 0 - only single NALU allowed
};
+struct H265VpsInfo {
+ size_t size = 0;
+ std::vector<uint8_t> data;
+};
+
+// Maybe alias as this is the same as H264PpsInfo?
+struct H265PpsInfo {
+ int sps_id = -1;
+ size_t size = 0;
+ std::vector<uint8_t> data;
+};
+
+// What about inheritance from H264SpsInfo as there is only one field added?
+struct H265SpsInfo {
+ int vps_id = -1;
+ size_t size = 0;
+ int width = -1;
+ int height = -1;
+ std::vector<uint8_t> data;
+};
+
struct RTPVideoHeaderH265 {
// The NAL unit type. If this is a header for a fragmented packet, it's the
// NAL unit type of the original data. If this is the header for an aggregated
size_t nalus_length;
// The packetization type of this buffer - single, aggregated or fragmented.
H265PacketizationMode packetization_mode;
+ bool first_fragment; // Is this first NAL framgment/packet
+ // valid only for `first_fragment` of kH265FU
+ uint8_t fau_nal_header[2];
+ absl::optional<std::tuple<H265VpsInfo, H265PpsInfo, H265SpsInfo>> extra_data;
};
} // namespace webrtc
+++ /dev/null
-/*
- * 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) {
- RTC_DCHECK(video_header->is_first_packet_in_frame);
- 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
+++ /dev/null
-/*
- * 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.
- */
-
-#ifndef MODULES_VIDEO_CODING_H264_SPS_PPS_TRACKER_H_
-#define MODULES_VIDEO_CODING_H264_SPS_PPS_TRACKER_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "api/array_view.h"
-#include "modules/rtp_rtcp/source/rtp_video_header.h"
-#include "rtc_base/copy_on_write_buffer.h"
-
-namespace webrtc {
-namespace video_coding {
-
-class H264SpsPpsTracker {
- public:
- enum PacketAction { kInsert, kDrop, kRequestKeyframe };
- struct FixedBitstream {
- PacketAction action;
- rtc::CopyOnWriteBuffer bitstream;
- };
-
- H264SpsPpsTracker();
- ~H264SpsPpsTracker();
-
- // Returns fixed bitstream and modifies `video_header`.
- FixedBitstream CopyAndFixBitstream(rtc::ArrayView<const uint8_t> bitstream,
- RTPVideoHeader* video_header);
-
- void InsertSpsPpsNalus(const std::vector<uint8_t>& sps,
- const std::vector<uint8_t>& pps);
-
- private:
- struct PpsInfo {
- PpsInfo();
- PpsInfo(PpsInfo&& rhs);
- PpsInfo& operator=(PpsInfo&& rhs);
- ~PpsInfo();
-
- int sps_id = -1;
- size_t size = 0;
- std::unique_ptr<uint8_t[]> data;
- };
-
- struct SpsInfo {
- SpsInfo();
- SpsInfo(SpsInfo&& rhs);
- SpsInfo& operator=(SpsInfo&& rhs);
- ~SpsInfo();
-
- size_t size = 0;
- int width = -1;
- int height = -1;
- std::unique_ptr<uint8_t[]> data;
- };
-
- std::map<uint32_t, PpsInfo> pps_data_;
- std::map<uint32_t, SpsInfo> sps_data_;
-};
-
-} // namespace video_coding
-} // namespace webrtc
-
-#endif // MODULES_VIDEO_CODING_H264_SPS_PPS_TRACKER_H_
+++ /dev/null
-/*
- * 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 <string.h>
-
-#include <vector>
-
-#include "absl/types/variant.h"
-#include "common_video/h264/h264_common.h"
-#include "modules/rtp_rtcp/source/rtp_video_header.h"
-#include "modules/video_coding/codecs/h264/include/h264_globals.h"
-#include "modules/video_coding/packet.h"
-#include "test/gmock.h"
-#include "test/gtest.h"
-
-namespace webrtc {
-namespace video_coding {
-namespace {
-
-using ::testing::ElementsAreArray;
-
-const uint8_t start_code[] = {0, 0, 0, 1};
-
-rtc::ArrayView<const uint8_t> Bitstream(
- const H264SpsPpsTracker::FixedBitstream& fixed) {
- return fixed.bitstream;
-}
-
-void ExpectSpsPpsIdr(const RTPVideoHeaderH264& codec_header,
- uint8_t sps_id,
- uint8_t pps_id) {
- bool contains_sps = false;
- bool contains_pps = false;
- bool contains_idr = false;
- for (const auto& nalu : codec_header.nalus) {
- if (nalu.type == H264::NaluType::kSps) {
- EXPECT_EQ(sps_id, nalu.sps_id);
- contains_sps = true;
- } else if (nalu.type == H264::NaluType::kPps) {
- EXPECT_EQ(sps_id, nalu.sps_id);
- EXPECT_EQ(pps_id, nalu.pps_id);
- contains_pps = true;
- } else if (nalu.type == H264::NaluType::kIdr) {
- EXPECT_EQ(pps_id, nalu.pps_id);
- contains_idr = true;
- }
- }
- EXPECT_TRUE(contains_sps);
- EXPECT_TRUE(contains_pps);
- EXPECT_TRUE(contains_idr);
-}
-
-class H264VideoHeader : public RTPVideoHeader {
- public:
- H264VideoHeader() {
- codec = kVideoCodecH264;
- is_first_packet_in_frame = false;
- auto& h264_header = video_type_header.emplace<RTPVideoHeaderH264>();
- h264_header.nalus_length = 0;
- h264_header.packetization_type = kH264SingleNalu;
- }
-
- RTPVideoHeaderH264& h264() {
- return absl::get<RTPVideoHeaderH264>(video_type_header);
- }
-};
-
-} // namespace
-
-class TestH264SpsPpsTracker : public ::testing::Test {
- public:
- void AddSps(H264VideoHeader* header,
- uint8_t sps_id,
- std::vector<uint8_t>* data) {
- NaluInfo info;
- info.type = H264::NaluType::kSps;
- info.sps_id = sps_id;
- info.pps_id = -1;
- data->push_back(H264::NaluType::kSps);
- data->push_back(sps_id); // The sps data, just a single byte.
-
- header->h264().nalus[header->h264().nalus_length++] = info;
- }
-
- void AddPps(H264VideoHeader* header,
- uint8_t sps_id,
- uint8_t pps_id,
- std::vector<uint8_t>* data) {
- NaluInfo info;
- info.type = H264::NaluType::kPps;
- info.sps_id = sps_id;
- info.pps_id = pps_id;
- data->push_back(H264::NaluType::kPps);
- data->push_back(pps_id); // The pps data, just a single byte.
-
- header->h264().nalus[header->h264().nalus_length++] = info;
- }
-
- void AddIdr(H264VideoHeader* header, int pps_id) {
- NaluInfo info;
- info.type = H264::NaluType::kIdr;
- info.sps_id = -1;
- info.pps_id = pps_id;
-
- header->h264().nalus[header->h264().nalus_length++] = info;
- }
-
- protected:
- H264SpsPpsTracker tracker_;
-};
-
-TEST_F(TestH264SpsPpsTracker, NoNalus) {
- uint8_t data[] = {1, 2, 3};
- H264VideoHeader header;
- header.h264().packetization_type = kH264FuA;
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &header);
-
- EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert);
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(data));
-}
-
-TEST_F(TestH264SpsPpsTracker, FuAFirstPacket) {
- uint8_t data[] = {1, 2, 3};
- H264VideoHeader header;
- header.h264().packetization_type = kH264FuA;
- header.h264().nalus_length = 1;
- header.is_first_packet_in_frame = true;
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &header);
-
- EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert);
- std::vector<uint8_t> expected;
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {1, 2, 3});
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected));
-}
-
-TEST_F(TestH264SpsPpsTracker, StapAIncorrectSegmentLength) {
- uint8_t data[] = {0, 0, 2, 0};
- H264VideoHeader header;
- header.h264().packetization_type = kH264StapA;
- header.is_first_packet_in_frame = true;
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action,
- H264SpsPpsTracker::kDrop);
-}
-
-TEST_F(TestH264SpsPpsTracker, SingleNaluInsertStartCode) {
- uint8_t data[] = {1, 2, 3};
- H264VideoHeader header;
- header.h264().nalus_length = 1;
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &header);
-
- EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert);
- std::vector<uint8_t> expected;
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {1, 2, 3});
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected));
-}
-
-TEST_F(TestH264SpsPpsTracker, NoStartCodeInsertedForSubsequentFuAPacket) {
- std::vector<uint8_t> data = {1, 2, 3};
- H264VideoHeader header;
- header.h264().packetization_type = kH264FuA;
- // Since no NALU begin in this packet the nalus_length is zero.
- header.h264().nalus_length = 0;
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &header);
-
- EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert);
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(data));
-}
-
-TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsPpsInserted) {
- std::vector<uint8_t> data = {1, 2, 3};
- H264VideoHeader header;
- header.is_first_packet_in_frame = true;
- AddIdr(&header, 0);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action,
- H264SpsPpsTracker::kRequestKeyframe);
-}
-
-TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoPpsInserted) {
- std::vector<uint8_t> data = {1, 2, 3};
- H264VideoHeader header;
- header.is_first_packet_in_frame = true;
- AddSps(&header, 0, &data);
- AddIdr(&header, 0);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action,
- H264SpsPpsTracker::kRequestKeyframe);
-}
-
-TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsInserted) {
- std::vector<uint8_t> data = {1, 2, 3};
- H264VideoHeader header;
- header.is_first_packet_in_frame = true;
- AddPps(&header, 0, 0, &data);
- AddIdr(&header, 0);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action,
- H264SpsPpsTracker::kRequestKeyframe);
-}
-
-TEST_F(TestH264SpsPpsTracker, SpsPpsPacketThenIdrFirstPacket) {
- std::vector<uint8_t> data;
- H264VideoHeader sps_pps_header;
- // Insert SPS/PPS
- AddSps(&sps_pps_header, 0, &data);
- AddPps(&sps_pps_header, 0, 1, &data);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &sps_pps_header).action,
- H264SpsPpsTracker::kInsert);
-
- // Insert first packet of the IDR
- H264VideoHeader idr_header;
- idr_header.is_first_packet_in_frame = true;
- AddIdr(&idr_header, 1);
- data = {1, 2, 3};
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &idr_header);
- EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert);
-
- std::vector<uint8_t> expected;
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {1, 2, 3});
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected));
-}
-
-TEST_F(TestH264SpsPpsTracker, SpsPpsIdrInStapA) {
- std::vector<uint8_t> data;
- H264VideoHeader header;
- header.h264().packetization_type = kH264StapA;
- header.is_first_packet_in_frame = true; // Always true for StapA
-
- data.insert(data.end(), {0}); // First byte is ignored
- data.insert(data.end(), {0, 2}); // Length of segment
- AddSps(&header, 13, &data);
- data.insert(data.end(), {0, 2}); // Length of segment
- AddPps(&header, 13, 27, &data);
- data.insert(data.end(), {0, 5}); // Length of segment
- AddIdr(&header, 27);
- data.insert(data.end(), {1, 2, 3, 2, 1});
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(data, &header);
-
- EXPECT_THAT(fixed.action, H264SpsPpsTracker::kInsert);
-
- std::vector<uint8_t> expected;
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {H264::NaluType::kSps, 13});
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {H264::NaluType::kPps, 27});
- expected.insert(expected.end(), start_code, start_code + sizeof(start_code));
- expected.insert(expected.end(), {1, 2, 3, 2, 1});
- EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected));
-}
-
-TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBand) {
- constexpr uint8_t kData[] = {1, 2, 3};
-
- // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
- // width: 320, height: 240
- const std::vector<uint8_t> sps(
- {0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00,
- 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60});
- const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
- tracker_.InsertSpsPpsNalus(sps, pps);
-
- // Insert first packet of the IDR.
- H264VideoHeader idr_header;
- idr_header.is_first_packet_in_frame = true;
- AddIdr(&idr_header, 0);
- EXPECT_EQ(idr_header.h264().nalus_length, 1u);
-
- H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(kData, &idr_header);
-
- EXPECT_EQ(idr_header.h264().nalus_length, 3u);
- EXPECT_EQ(idr_header.width, 320u);
- EXPECT_EQ(idr_header.height, 240u);
- ExpectSpsPpsIdr(idr_header.h264(), 0, 0);
-}
-
-TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandWrongNaluHeader) {
- constexpr uint8_t kData[] = {1, 2, 3};
-
- // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
- // Nalu headers manupilated afterwards.
- const std::vector<uint8_t> sps(
- {0xff, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00,
- 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60});
- const std::vector<uint8_t> pps({0xff, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
- tracker_.InsertSpsPpsNalus(sps, pps);
-
- // Insert first packet of the IDR.
- H264VideoHeader idr_header;
- idr_header.is_first_packet_in_frame = true;
- AddIdr(&idr_header, 0);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(kData, &idr_header).action,
- H264SpsPpsTracker::kRequestKeyframe);
-}
-
-TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandIncompleteNalu) {
- constexpr uint8_t kData[] = {1, 2, 3};
-
- // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos.
- // Nalus damaged afterwards.
- const std::vector<uint8_t> sps({0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9});
- const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0});
- tracker_.InsertSpsPpsNalus(sps, pps);
-
- // Insert first packet of the IDR.
- H264VideoHeader idr_header;
- idr_header.is_first_packet_in_frame = true;
- AddIdr(&idr_header, 0);
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(kData, &idr_header).action,
- H264SpsPpsTracker::kRequestKeyframe);
-}
-
-TEST_F(TestH264SpsPpsTracker, SaveRestoreWidthHeight) {
- std::vector<uint8_t> data;
-
- // Insert an SPS/PPS packet with width/height and make sure
- // that information is set on the first IDR packet.
- H264VideoHeader sps_pps_header;
- AddSps(&sps_pps_header, 0, &data);
- AddPps(&sps_pps_header, 0, 1, &data);
- sps_pps_header.width = 320;
- sps_pps_header.height = 240;
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &sps_pps_header).action,
- H264SpsPpsTracker::kInsert);
-
- H264VideoHeader idr_header;
- idr_header.is_first_packet_in_frame = true;
- AddIdr(&idr_header, 1);
- data.insert(data.end(), {1, 2, 3});
-
- EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &idr_header).action,
- H264SpsPpsTracker::kInsert);
-
- EXPECT_EQ(idr_header.width, 320);
- EXPECT_EQ(idr_header.height, 240);
-}
-
-} // namespace video_coding
-} // namespace webrtc
+++ /dev/null
-/*
- * Copyright (c) 2018 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/h265_vps_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/h265/h265_common.h"
-#include "common_video/h265/h265_pps_parser.h"
-#include "common_video/h265/h265_sps_parser.h"
-#include "common_video/h265/h265_vps_parser.h"
-#include "modules/video_coding/codecs/h264/include/h264_globals.h"
-#include "modules/video_coding/codecs/h265/include/h265_globals.h"
-#include "modules/video_coding/frame_object.h"
-#include "modules/video_coding/packet_buffer.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/logging.h"
-
-namespace webrtc {
-namespace video_coding {
-
-namespace {
-const uint8_t start_code_h265[] = {0, 0, 0, 1};
-} // namespace
-
-H265VpsSpsPpsTracker::FixedBitstream H265VpsSpsPpsTracker::CopyAndFixBitstream(
- rtc::ArrayView<const uint8_t> bitstream,
- RTPVideoHeader* video_header) {
- RTC_DCHECK(video_header);
- RTC_DCHECK(video_header->codec == kVideoCodecH265);
-
- auto& h265_header =
- absl::get<RTPVideoHeaderH265>(video_header->video_type_header);
-
- bool append_vps_sps_pps = false;
- auto vps = vps_data_.end();
- auto sps = sps_data_.end();
- auto pps = pps_data_.end();
-
- for (size_t i = 0; i < h265_header.nalus_length; ++i) {
- const H265NaluInfo& nalu = h265_header.nalus[i];
- switch (nalu.type) {
- case H265::NaluType::kVps: {
- vps_data_[nalu.vps_id].size = 0;
- break;
- }
- case H265::NaluType::kSps: {
- sps_data_[nalu.sps_id].vps_id = nalu.vps_id;
- sps_data_[nalu.sps_id].width = video_header->width;
- sps_data_[nalu.sps_id].height = video_header->height;
- break;
- }
- case H265::NaluType::kPps: {
- pps_data_[nalu.pps_id].sps_id = nalu.sps_id;
- break;
- }
- case H265::NaluType::kIdrWRadl:
- case H265::NaluType::kIdrNLp:
- case H265::NaluType::kCra: {
- // 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};
- }
-
- vps = vps_data_.find(sps->second.vps_id);
- if (vps == vps_data_.end()) {
- RTC_LOG(LS_WARNING)
- << "No VPS with id " << sps->second.vps_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 VPS/SPS/PPS was supplied out of band then we will have saved
- // the actual bitstream in |data|.
- // This branch is not verified.
- if (vps->second.data && sps->second.data && pps->second.data) {
- RTC_DCHECK_GT(vps->second.size, 0);
- RTC_DCHECK_GT(sps->second.size, 0);
- RTC_DCHECK_GT(pps->second.size, 0);
- append_vps_sps_pps = true;
- }
- }
- break;
- }
- default:
- break;
- }
- }
-
- RTC_CHECK(!append_vps_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_vps_sps_pps) {
- required_size += vps->second.size + sizeof(start_code_h265);
- required_size += sps->second.size + sizeof(start_code_h265);
- required_size += pps->second.size + sizeof(start_code_h265);
- }
-
- if (h265_header.packetization_type == kH265AP) {
- const uint8_t* nalu_ptr = bitstream.data() + 1;
- while (nalu_ptr < bitstream.data() + bitstream.size()) {
- RTC_DCHECK(video_header->is_first_packet_in_frame);
- required_size += sizeof(start_code_h265);
-
- // 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 {
- // TODO: in h.264 this is "h264_header.nalus_length > 0"
- if (video_header->is_first_packet_in_frame)
- required_size += sizeof(start_code_h265);
- required_size += bitstream.size();
- }
-
- // Then we copy to the new buffer.
- H265VpsSpsPpsTracker::FixedBitstream fixed;
- fixed.bitstream.EnsureCapacity(required_size);
-
- if (append_vps_sps_pps) {
- // Insert VPS.
- fixed.bitstream.AppendData(start_code_h265);
- fixed.bitstream.AppendData(vps->second.data.get(), vps->second.size);
-
- // Insert SPS.
- fixed.bitstream.AppendData(start_code_h265);
- fixed.bitstream.AppendData(sps->second.data.get(), sps->second.size);
-
- // Insert PPS.
- fixed.bitstream.AppendData(start_code_h265);
- fixed.bitstream.AppendData(pps->second.data.get(), pps->second.size);
-
- // Update codec header to reflect the newly added SPS and PPS.
- H265NaluInfo vps_info;
- vps_info.type = H265::NaluType::kVps;
- vps_info.vps_id = vps->first;
- vps_info.sps_id = -1;
- vps_info.pps_id = -1;
- H265NaluInfo sps_info;
- sps_info.type = H265::NaluType::kSps;
- sps_info.vps_id = vps->first;
- sps_info.sps_id = sps->first;
- sps_info.pps_id = -1;
- H265NaluInfo pps_info;
- pps_info.type = H265::NaluType::kPps;
- pps_info.vps_id = vps->first;
- pps_info.sps_id = sps->first;
- pps_info.pps_id = pps->first;
- if (h265_header.nalus_length + 2 <= kMaxNalusPerPacket) {
- h265_header.nalus[h265_header.nalus_length++] = vps_info;
- h265_header.nalus[h265_header.nalus_length++] = sps_info;
- h265_header.nalus[h265_header.nalus_length++] = pps_info;
- } else {
- RTC_LOG(LS_WARNING) << "Not enough space in H.265 codec header to insert "
- "SPS/PPS provided out-of-band.";
- }
- }
-
- // Copy the rest of the bitstream and insert start codes.
- if (h265_header.packetization_type == kH265AP) {
- const uint8_t* nalu_ptr = bitstream.data() + 1;
- while (nalu_ptr < bitstream.data() + bitstream.size()) {
- fixed.bitstream.AppendData(start_code_h265);
-
- // 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 {
- // For h.264 it is "h264_header.nalus_length > 0"
- if (video_header->is_first_packet_in_frame) {
- fixed.bitstream.AppendData(start_code_h265);
- }
- fixed.bitstream.AppendData(bitstream.data(), bitstream.size());
- }
-
- fixed.action = kInsert;
- return fixed;
-}
-
-} // namespace video_coding
-} // namespace webrtc
+++ /dev/null
-/*
- * Copyright (c) 2018 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.
- */
-
-#ifndef THIRD_PARTY_WEBRTC_MODULES_VIDEO_CODING_H265_VPS_SPS_PPS_TRACKER_H_
-#define THIRD_PARTY_WEBRTC_MODULES_VIDEO_CODING_H265_VPS_SPS_PPS_TRACKER_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "api/array_view.h"
-#include "modules/rtp_rtcp/source/rtp_video_header.h"
-#include "rtc_base/copy_on_write_buffer.h"
-
-namespace webrtc {
-namespace video_coding {
-
-class H265VpsSpsPpsTracker {
- public:
- enum PacketAction { kInsert, kDrop, kRequestKeyframe };
- struct FixedBitstream {
- PacketAction action;
- rtc::CopyOnWriteBuffer bitstream;
- };
-
- FixedBitstream CopyAndFixBitstream(rtc::ArrayView<const uint8_t> bitstream,
- RTPVideoHeader* video_header);
-
- private:
- struct VpsInfo {
- size_t size = 0;
- std::unique_ptr<uint8_t[]> data;
- };
-
- struct PpsInfo {
- int sps_id = -1;
- size_t size = 0;
- std::unique_ptr<uint8_t[]> data;
- };
-
- struct SpsInfo {
- int vps_id = -1;
- size_t size = 0;
- int width = -1;
- int height = -1;
- std::unique_ptr<uint8_t[]> data;
- };
-
- std::map<uint32_t, VpsInfo> vps_data_;
- std::map<uint32_t, PpsInfo> pps_data_;
- std::map<uint32_t, SpsInfo> sps_data_;
-};
-
-} // namespace video_coding
-} // namespace webrtc
-
-#endif // THIRD_PARTY_WEBRTC_MODULES_VIDEO_CODING_H265_VPS_SPS_PPS_TRACKER_H_
#include "modules/utility/include/process_thread.h"
#include "modules/video_coding/deprecated/nack_module.h"
#include "modules/video_coding/frame_object.h"
-#include "modules/video_coding/h264_sprop_parameter_sets.h"
-#include "modules/video_coding/h264_sps_pps_tracker.h"
#include "modules/video_coding/packet_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/location.h"
payload_type, raw_payload
? std::make_unique<VideoRtpDepacketizerRaw>()
: CreateVideoRtpDepacketizer(video_codec.codecType));
- pt_codec_params_.emplace(payload_type, codec_params);
+ payload_type_map_[payload_type]->SetCodecParams(codec_params);
}
absl::optional<Syncable::Info> RtpVideoStreamReceiver::GetSyncInfo() const {
return;
}
- if (packet->codec() == kVideoCodecH264) {
- // Only when we start to receive packets will we know what payload type
- // that will be used. When we know the payload type insert the correct
- // sps/pps into the tracker.
- if (packet->payload_type != last_payload_type_) {
- last_payload_type_ = packet->payload_type;
- InsertSpsPpsIntoTracker(packet->payload_type);
- }
-
- video_coding::H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(
- rtc::MakeArrayView(codec_payload.cdata(), codec_payload.size()),
- &packet->video_header);
-
- switch (fixed.action) {
- case video_coding::H264SpsPpsTracker::kRequestKeyframe:
- rtcp_feedback_buffer_.RequestKeyFrame();
- rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
- ABSL_FALLTHROUGH_INTENDED;
- case video_coding::H264SpsPpsTracker::kDrop:
- return;
- case video_coding::H264SpsPpsTracker::kInsert:
- packet->video_payload = std::move(fixed.bitstream);
- break;
- }
-
-
-#if defined(OS_TIZEN_TV_PRODUCT) && BUILDFLAG(ENABLE_PLATFORM_HEVC)
- } else if (packet->codec() == kVideoCodecH265) {
- // Only when we start to receive packets will we know what payload type
- // that will be used. When we know the payload type insert the correct
- // sps/pps into the tracker.
- if (packet->payload_type != last_payload_type_) {
- last_payload_type_ = packet->payload_type;
- InsertSpsPpsIntoTracker(packet->payload_type);
- }
- video_coding::H265VpsSpsPpsTracker::FixedBitstream fixed =
- h265_tracker_.CopyAndFixBitstream(
- rtc::MakeArrayView(codec_payload.cdata(), codec_payload.size()),
- &packet->video_header);
- switch (fixed.action) {
- case video_coding::H265VpsSpsPpsTracker::kRequestKeyframe:
- rtcp_feedback_buffer_.RequestKeyFrame();
- rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
- ABSL_FALLTHROUGH_INTENDED;
- case video_coding::H265VpsSpsPpsTracker::kDrop:
- return;
- case video_coding::H265VpsSpsPpsTracker::kInsert:
- packet->video_payload = std::move(fixed.bitstream);
- break;
- }
-#endif
-
- } else {
- packet->video_payload = std::move(codec_payload);
+ const auto type_it = payload_type_map_.find(rtp_packet.PayloadType());
+ if (type_it == payload_type_map_.end()) {
+ return;
+ }
+ auto packet_action =
+ type_it->second->GetPacketAction(codec_payload, &packet->video_header);
+ switch (packet_action) {
+ case VideoRtpDepacketizer::kRequestKeyframe:
+ rtcp_feedback_buffer_.RequestKeyFrame();
+ rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
+ ABSL_FALLTHROUGH_INTENDED;
+ case VideoRtpDepacketizer::kDrop:
+ return;
+ case VideoRtpDepacketizer::kInsert:
+ packet->video_payload = std::move(codec_payload);
+ break;
}
rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
int max_nack_count;
int64_t min_recv_time;
int64_t max_recv_time;
- std::vector<rtc::ArrayView<const uint8_t>> payloads;
+ std::vector<VideoRtpDepacketizer::RtpPacketView> payloads;
RtpPacketInfos::vector_type packet_infos;
bool frame_boundary = true;
max_recv_time =
std::max(max_recv_time, packet_info.receive_time().ms());
}
- payloads.emplace_back(packet->video_payload);
+ payloads.emplace_back(&packet->video_header, packet->video_payload);
packet_infos.push_back(packet_info);
frame_boundary = packet->is_last_packet_in_frame();
}
}
-void RtpVideoStreamReceiver::InsertSpsPpsIntoTracker(uint8_t payload_type) {
- auto codec_params_it = pt_codec_params_.find(payload_type);
- if (codec_params_it == pt_codec_params_.end())
- return;
-
- RTC_LOG(LS_INFO) << "Found out of band supplied codec parameters for"
- " payload type: "
- << static_cast<int>(payload_type);
-
- H264SpropParameterSets sprop_decoder;
- auto sprop_base64_it =
- codec_params_it->second.find(cricket::kH264FmtpSpropParameterSets);
-
- if (sprop_base64_it == codec_params_it->second.end())
- return;
-
- if (!sprop_decoder.DecodeSprop(sprop_base64_it->second.c_str()))
- return;
-
- tracker_.InsertSpsPpsNalus(sprop_decoder.sps_nalu(),
- sprop_decoder.pps_nalu());
-}
-
void RtpVideoStreamReceiver::UpdatePacketReceiveTimestamps(
const RtpPacketReceived& packet,
bool is_keyframe) {
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
-#include "modules/video_coding/h264_sps_pps_tracker.h"
#include "modules/video_coding/loss_notification_controller.h"
#include "modules/video_coding/packet_buffer.h"
#include "modules/video_coding/rtp_frame_reference_finder.h"
void NotifyReceiverOfEmptyPacket(uint16_t seq_num);
void UpdateHistograms();
bool IsRedEnabled() const;
- void InsertSpsPpsIntoTracker(uint8_t payload_type);
void OnInsertedPacket(video_coding::PacketBuffer::InsertResult result);
ParseGenericDependenciesResult ParseGenericDependenciesExtension(
const RtpPacketReceived& rtp_packet,
Mutex last_seq_num_mutex_;
std::map<int64_t, uint16_t> last_seq_num_for_pic_id_
RTC_GUARDED_BY(last_seq_num_mutex_);
- video_coding::H264SpsPpsTracker tracker_;
// Maps payload id to the depacketizer.
flat_map<uint8_t, std::unique_ptr<VideoRtpDepacketizer>> payload_type_map_;
-#if defined(OS_TIZEN_TV_PRODUCT) && BUILDFLAG(ENABLE_PLATFORM_HEVC)
- video_coding::H265VpsSpsPpsTracker h265_tracker_;
-#endif
-
- // TODO(johan): Remove pt_codec_params_ once
- // https://bugs.chromium.org/p/webrtc/issues/detail?id=6883 is resolved.
- // Maps a payload type to a map of out-of-band supplied codec parameters.
- flat_map<uint8_t, std::map<std::string, std::string>> pt_codec_params_;
- int16_t last_payload_type_ = -1;
-
bool has_received_frame_;
std::vector<RtpPacketSinkInterface*> secondary_sinks_
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_raw.h"
#include "modules/video_coding/frame_object.h"
-#include "modules/video_coding/h264_sprop_parameter_sets.h"
-#include "modules/video_coding/h264_sps_pps_tracker.h"
#include "modules/video_coding/nack_requester.h"
#include "modules/video_coding/packet_buffer.h"
#include "rtc_base/checks.h"
payload_type_map_.emplace(
payload_type, raw_payload ? std::make_unique<VideoRtpDepacketizerRaw>()
: CreateVideoRtpDepacketizer(video_codec));
- pt_codec_params_.emplace(payload_type, codec_params);
+ payload_type_map_[payload_type]->SetCodecParams(codec_params);
}
absl::optional<Syncable::Info> RtpVideoStreamReceiver2::GetSyncInfo() const {
return;
}
- if (packet->codec() == kVideoCodecH264) {
- // Only when we start to receive packets will we know what payload type
- // that will be used. When we know the payload type insert the correct
- // sps/pps into the tracker.
- if (packet->payload_type != last_payload_type_) {
- last_payload_type_ = packet->payload_type;
- InsertSpsPpsIntoTracker(packet->payload_type);
- }
-
- video_coding::H264SpsPpsTracker::FixedBitstream fixed =
- tracker_.CopyAndFixBitstream(
- rtc::MakeArrayView(codec_payload.cdata(), codec_payload.size()),
- &packet->video_header);
-
- switch (fixed.action) {
- case video_coding::H264SpsPpsTracker::kRequestKeyframe:
- rtcp_feedback_buffer_.RequestKeyFrame();
- rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
- ABSL_FALLTHROUGH_INTENDED;
- case video_coding::H264SpsPpsTracker::kDrop:
- return;
- case video_coding::H264SpsPpsTracker::kInsert:
- packet->video_payload = std::move(fixed.bitstream);
- break;
- }
-
-#if defined(OS_TIZEN_TV_PRODUCT) && BUILDFLAG(ENABLE_PLATFORM_HEVC)
- } else if (packet->codec() == kVideoCodecH265) {
- // Only when we start to receive packets will we know what payload type
- // that will be used. When we know the payload type insert the correct
- // sps/pps into the tracker.
- if (packet->payload_type != last_payload_type_) {
- last_payload_type_ = packet->payload_type;
- InsertSpsPpsIntoTracker(packet->payload_type);
- }
- video_coding::H265VpsSpsPpsTracker::FixedBitstream fixed =
- h265_tracker_.CopyAndFixBitstream(
- rtc::MakeArrayView(codec_payload.cdata(), codec_payload.size()),
- &packet->video_header);
- switch (fixed.action) {
- case video_coding::H265VpsSpsPpsTracker::kRequestKeyframe:
- rtcp_feedback_buffer_.RequestKeyFrame();
- rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
- ABSL_FALLTHROUGH_INTENDED;
- case video_coding::H265VpsSpsPpsTracker::kDrop:
- return;
- case video_coding::H265VpsSpsPpsTracker::kInsert:
- packet->video_payload = std::move(fixed.bitstream);
- break;
- }
-#endif
-
- } else {
- packet->video_payload = std::move(codec_payload);
+ const auto type_it = payload_type_map_.find(rtp_packet.PayloadType());
+ if (type_it == payload_type_map_.end()) {
+ return;
+ }
+ auto packet_action =
+ type_it->second->GetPacketAction(codec_payload, &packet->video_header);
+ switch (packet_action) {
+ case VideoRtpDepacketizer::kRequestKeyframe:
+ rtcp_feedback_buffer_.RequestKeyFrame();
+ rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
+ ABSL_FALLTHROUGH_INTENDED;
+ case VideoRtpDepacketizer::kDrop:
+ return;
+ case VideoRtpDepacketizer::kInsert:
+ packet->video_payload = std::move(codec_payload);
+ break;
}
rtcp_feedback_buffer_.SendBufferedRtcpFeedback();
int max_nack_count;
int64_t min_recv_time;
int64_t max_recv_time;
- std::vector<rtc::ArrayView<const uint8_t>> payloads;
+ std::vector<VideoRtpDepacketizer::RtpPacketView> payloads;
RtpPacketInfos::vector_type packet_infos;
bool frame_boundary = true;
min_recv_time = std::min(min_recv_time, packet_info.receive_time().ms());
max_recv_time = std::max(max_recv_time, packet_info.receive_time().ms());
}
- payloads.emplace_back(packet->video_payload);
+ payloads.emplace_back(&packet->video_header, packet->video_payload);
packet_infos.push_back(packet_info);
frame_boundary = packet->is_last_packet_in_frame();
}
}
-// RTC_RUN_ON(packet_sequence_checker_)
-void RtpVideoStreamReceiver2::InsertSpsPpsIntoTracker(uint8_t payload_type) {
- RTC_DCHECK_RUN_ON(&worker_task_checker_);
-
- auto codec_params_it = pt_codec_params_.find(payload_type);
- if (codec_params_it == pt_codec_params_.end())
- return;
-
- RTC_LOG(LS_INFO) << "Found out of band supplied codec parameters for"
- " payload type: "
- << static_cast<int>(payload_type);
-
- H264SpropParameterSets sprop_decoder;
- auto sprop_base64_it =
- codec_params_it->second.find(cricket::kH264FmtpSpropParameterSets);
-
- if (sprop_base64_it == codec_params_it->second.end())
- return;
-
- if (!sprop_decoder.DecodeSprop(sprop_base64_it->second.c_str()))
- return;
-
- tracker_.InsertSpsPpsNalus(sprop_decoder.sps_nalu(),
- sprop_decoder.pps_nalu());
-}
-
void RtpVideoStreamReceiver2::UpdatePacketReceiveTimestamps(
const RtpPacketReceived& packet,
bool is_keyframe) {
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
-#include "modules/video_coding/h264_sps_pps_tracker.h"
#include "modules/video_coding/loss_notification_controller.h"
#include "modules/video_coding/nack_requester.h"
#include "modules/video_coding/packet_buffer.h"
#include "video/buffered_frame_decryptor.h"
#include "video/rtp_video_stream_receiver_frame_transformer_delegate.h"
-#if defined(OS_TIZEN_TV_PRODUCT) && BUILDFLAG(ENABLE_PLATFORM_HEVC)
-#include "modules/video_coding/h265_vps_sps_pps_tracker.h"
-#endif
-
namespace webrtc {
class NackRequester;
RTC_RUN_ON(packet_sequence_checker_);
void UpdateHistograms();
bool IsRedEnabled() const;
- void InsertSpsPpsIntoTracker(uint8_t payload_type)
- RTC_RUN_ON(packet_sequence_checker_);
void OnInsertedPacket(video_coding::PacketBuffer::InsertResult result)
RTC_RUN_ON(packet_sequence_checker_);
ParseGenericDependenciesResult ParseGenericDependenciesExtension(
std::map<int64_t, uint16_t> last_seq_num_for_pic_id_
RTC_GUARDED_BY(packet_sequence_checker_);
- video_coding::H264SpsPpsTracker tracker_
- RTC_GUARDED_BY(packet_sequence_checker_);
// Maps payload id to the depacketizer.
flat_map<uint8_t, std::unique_ptr<VideoRtpDepacketizer>> payload_type_map_
RTC_GUARDED_BY(packet_sequence_checker_);
-#if defined(OS_TIZEN_TV_PRODUCT) && BUILDFLAG(ENABLE_PLATFORM_HEVC)
- video_coding::H265VpsSpsPpsTracker h265_tracker_;
-#endif
-
- // TODO(johan): Remove pt_codec_params_ once
- // https://bugs.chromium.org/p/webrtc/issues/detail?id=6883 is resolved.
- // Maps a payload type to a map of out-of-band supplied codec parameters.
- flat_map<uint8_t, std::map<std::string, std::string>> pt_codec_params_
- RTC_GUARDED_BY(packet_sequence_checker_);
- int16_t last_payload_type_ RTC_GUARDED_BY(packet_sequence_checker_) = -1;
-
bool has_received_frame_ RTC_GUARDED_BY(packet_sequence_checker_);
absl::optional<uint32_t> last_received_rtp_timestamp_
public:
RtpVideoStreamReceiver2Test() : RtpVideoStreamReceiver2Test("") {}
explicit RtpVideoStreamReceiver2Test(std::string field_trials)
+ : RtpVideoStreamReceiver2Test(field_trials, kVideoCodecGeneric) {}
+ RtpVideoStreamReceiver2Test(std::string field_trials, VideoCodecType codec)
: time_controller_(Timestamp::Millis(100)),
task_queue_(time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
"RtpVideoStreamReceiver2Test",
nullptr, &nack_periodic_processor_, &mock_nack_sender_,
&mock_key_frame_request_sender_, &mock_on_complete_frame_callback_,
nullptr, nullptr);
- rtp_video_stream_receiver_->AddReceiveCodec(kPayloadType,
- kVideoCodecGeneric, {},
+ rtp_video_stream_receiver_->AddReceiveCodec(kPayloadType, codec, {},
/*raw_payload=*/false);
}
: public RtpVideoStreamReceiver2Test,
public ::testing::WithParamInterface<std::string> {
protected:
- RtpVideoStreamReceiver2TestH264() : RtpVideoStreamReceiver2Test(GetParam()) {}
+ RtpVideoStreamReceiver2TestH264()
+ : RtpVideoStreamReceiver2Test(GetParam(), kVideoCodecH264) {}
};
INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe,
public:
RtpVideoStreamReceiverTest() : RtpVideoStreamReceiverTest("") {}
explicit RtpVideoStreamReceiverTest(std::string field_trials)
+ : RtpVideoStreamReceiverTest(field_trials, kVideoCodecGeneric) {}
+ RtpVideoStreamReceiverTest(std::string field_trials,
+ VideoCodecType codec_type)
: override_field_trials_(field_trials),
config_(CreateConfig()),
process_thread_(ProcessThread::Create("TestThread")) {
&mock_nack_sender_, &mock_key_frame_request_sender_,
&mock_on_complete_frame_callback_, nullptr, nullptr);
VideoCodec codec;
- codec.codecType = kVideoCodecGeneric;
+ codec.codecType = codec_type;
rtp_video_stream_receiver_->AddReceiveCodec(kPayloadType, codec, {},
/*raw_payload=*/false);
}
: public RtpVideoStreamReceiverTest,
public ::testing::WithParamInterface<std::string> {
protected:
- RtpVideoStreamReceiverTestH264() : RtpVideoStreamReceiverTest(GetParam()) {}
+ RtpVideoStreamReceiverTestH264()
+ : RtpVideoStreamReceiverTest(GetParam(), kVideoCodecH264) {}
};
INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe,
TEST_P(RtpVideoStreamReceiverTestH264, OutOfBandFmtpSpsPps) {
constexpr int kPayloadType = 99;
VideoCodec codec;
+ codec.codecType = kVideoCodecH264;
std::map<std::string, std::string> codec_params;
// Example parameter sets from https://tools.ietf.org/html/rfc3984#section-8.2
// .
TEST_P(RtpVideoStreamReceiverTestH264, ForceSpsPpsIdrIsKeyframe) {
constexpr int kPayloadType = 99;
VideoCodec codec;
+ codec.codecType = kVideoCodecH264;
std::map<std::string, std::string> codec_params;
if (GetParam() ==
"") { // Forcing can be done either with field trial or codec_params.