Change H264 depacketizer to implement VideoRtpDepacketizer interface

Bug: webrtc:11152
Change-Id: If5169f47d85918356fa66e2bf3422d722044aa1f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/165581
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Markus Handell <handellm@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30264}
This commit is contained in:
Danil Chapovalov
2020-01-15 11:34:28 +01:00
committed by Commit Bot
parent 07b17df771
commit 61d6471912
6 changed files with 286 additions and 342 deletions

View File

@ -12,49 +12,21 @@
#include <memory> #include <memory>
#include "absl/memory/memory.h" #include "api/video/video_codec_type.h"
#include "absl/types/optional.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_generic.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_generic.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h"
#include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
namespace webrtc { namespace webrtc {
namespace {
// Wrapper over legacy RtpDepacketizer interface.
// TODO(bugs.webrtc.org/11152): Delete when all RtpDepacketizers updated to
// the VideoRtpDepacketizer interface.
template <typename Depacketizer>
class Legacy : public VideoRtpDepacketizer {
public:
absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) override {
Depacketizer depacketizer;
RtpDepacketizer::ParsedPayload parsed_payload;
if (!depacketizer.Parse(&parsed_payload, rtp_payload.cdata(),
rtp_payload.size())) {
return absl::nullopt;
}
absl::optional<ParsedRtpPayload> result(absl::in_place);
result->video_header = parsed_payload.video;
result->video_payload.SetData(parsed_payload.payload,
parsed_payload.payload_length);
return result;
}
};
} // namespace
std::unique_ptr<VideoRtpDepacketizer> CreateVideoRtpDepacketizer( std::unique_ptr<VideoRtpDepacketizer> CreateVideoRtpDepacketizer(
VideoCodecType codec) { VideoCodecType codec) {
switch (codec) { switch (codec) {
case kVideoCodecH264: case kVideoCodecH264:
return std::make_unique<Legacy<RtpDepacketizerH264>>(); return std::make_unique<VideoRtpDepacketizerH264>();
case kVideoCodecVP8: case kVideoCodecVP8:
return std::make_unique<VideoRtpDepacketizerVp8>(); return std::make_unique<VideoRtpDepacketizerVp8>();
case kVideoCodecVP9: case kVideoCodecVP9:

View File

@ -22,7 +22,6 @@
#include "modules/include/module_common_types.h" #include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_format.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include "modules/video_coding/codecs/h264/include/h264_globals.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h"
#include "rtc_base/buffer.h" #include "rtc_base/buffer.h"
#include "rtc_base/constructor_magic.h" #include "rtc_base/constructor_magic.h"

View File

@ -10,11 +10,8 @@
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include <string.h>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -25,17 +22,19 @@
#include "common_video/h264/sps_parser.h" #include "common_video/h264/sps_parser.h"
#include "common_video/h264/sps_vui_rewriter.h" #include "common_video/h264/sps_vui_rewriter.h"
#include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
#include "rtc_base/system/fallthrough.h" #include "rtc_base/system/fallthrough.h"
namespace webrtc { namespace webrtc {
namespace { namespace {
static const size_t kNalHeaderSize = 1; constexpr size_t kNalHeaderSize = 1;
static const size_t kFuAHeaderSize = 2; constexpr size_t kFuAHeaderSize = 2;
static const size_t kLengthFieldSize = 2; constexpr size_t kLengthFieldSize = 2;
static const size_t kStapAHeaderSize = kNalHeaderSize + kLengthFieldSize; constexpr size_t kStapAHeaderSize = kNalHeaderSize + kLengthFieldSize;
// Bit masks for FU (A and B) indicators. // Bit masks for FU (A and B) indicators.
enum NalDefs : uint8_t { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; enum NalDefs : uint8_t { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F };
@ -66,36 +65,35 @@ bool ParseStapAStartOffsets(const uint8_t* nalu_ptr,
return true; return true;
} }
} // namespace absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> ProcessStapAOrSingleNalu(
rtc::CopyOnWriteBuffer rtp_payload) {
RtpDepacketizerH264::RtpDepacketizerH264() : offset_(0), length_(0) {} const uint8_t* const payload_data = rtp_payload.cdata();
RtpDepacketizerH264::~RtpDepacketizerH264() {} absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed_payload(
absl::in_place);
bool RtpDepacketizerH264::ProcessStapAOrSingleNalu( bool modified_buffer = false;
ParsedPayload* parsed_payload, parsed_payload->video_payload = rtp_payload;
const uint8_t* payload_data) { parsed_payload->video_header.width = 0;
parsed_payload->video_header().width = 0; parsed_payload->video_header.height = 0;
parsed_payload->video_header().height = 0; parsed_payload->video_header.codec = kVideoCodecH264;
parsed_payload->video_header().codec = kVideoCodecH264; parsed_payload->video_header.simulcastIdx = 0;
parsed_payload->video_header().simulcastIdx = 0; parsed_payload->video_header.is_first_packet_in_frame = true;
parsed_payload->video_header().is_first_packet_in_frame = true; auto& h264_header = parsed_payload->video_header.video_type_header
auto& h264_header = absl::get<RTPVideoHeaderH264>( .emplace<RTPVideoHeaderH264>();
parsed_payload->video_header().video_type_header);
const uint8_t* nalu_start = payload_data + kNalHeaderSize; const uint8_t* nalu_start = payload_data + kNalHeaderSize;
const size_t nalu_length = length_ - kNalHeaderSize; const size_t nalu_length = rtp_payload.size() - kNalHeaderSize;
uint8_t nal_type = payload_data[0] & kTypeMask; uint8_t nal_type = payload_data[0] & kTypeMask;
std::vector<size_t> nalu_start_offsets; std::vector<size_t> nalu_start_offsets;
if (nal_type == H264::NaluType::kStapA) { if (nal_type == H264::NaluType::kStapA) {
// Skip the StapA header (StapA NAL type + length). // Skip the StapA header (StapA NAL type + length).
if (length_ <= kStapAHeaderSize) { if (rtp_payload.size() <= kStapAHeaderSize) {
RTC_LOG(LS_ERROR) << "StapA header truncated."; RTC_LOG(LS_ERROR) << "StapA header truncated.";
return false; return absl::nullopt;
} }
if (!ParseStapAStartOffsets(nalu_start, nalu_length, &nalu_start_offsets)) { if (!ParseStapAStartOffsets(nalu_start, nalu_length, &nalu_start_offsets)) {
RTC_LOG(LS_ERROR) << "StapA packet with incorrect NALU packet lengths."; RTC_LOG(LS_ERROR) << "StapA packet with incorrect NALU packet lengths.";
return false; return absl::nullopt;
} }
h264_header.packetization_type = kH264StapA; h264_header.packetization_type = kH264StapA;
@ -105,9 +103,10 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
nalu_start_offsets.push_back(0); nalu_start_offsets.push_back(0);
} }
h264_header.nalu_type = nal_type; h264_header.nalu_type = nal_type;
parsed_payload->video_header().frame_type = VideoFrameType::kVideoFrameDelta; parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameDelta;
nalu_start_offsets.push_back(length_ + kLengthFieldSize); // End offset. nalu_start_offsets.push_back(rtp_payload.size() +
kLengthFieldSize); // End offset.
for (size_t i = 0; i < nalu_start_offsets.size() - 1; ++i) { for (size_t i = 0; i < nalu_start_offsets.size() - 1; ++i) {
size_t start_offset = nalu_start_offsets[i]; size_t start_offset = nalu_start_offsets[i];
// End offset is actually start offset for next unit, excluding length field // End offset is actually start offset for next unit, excluding length field
@ -115,7 +114,7 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
size_t end_offset = nalu_start_offsets[i + 1] - kLengthFieldSize; size_t end_offset = nalu_start_offsets[i + 1] - kLengthFieldSize;
if (end_offset - start_offset < H264::kNaluTypeSize) { if (end_offset - start_offset < H264::kNaluTypeSize) {
RTC_LOG(LS_ERROR) << "STAP-A packet too short"; RTC_LOG(LS_ERROR) << "STAP-A packet too short";
return false; return absl::nullopt;
} }
NaluInfo nalu; NaluInfo nalu;
@ -131,18 +130,18 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
// excessive decoder latency. // excessive decoder latency.
// Copy any previous data first (likely just the first header). // Copy any previous data first (likely just the first header).
std::unique_ptr<rtc::Buffer> output_buffer(new rtc::Buffer()); rtc::Buffer output_buffer;
if (start_offset) if (start_offset)
output_buffer->AppendData(payload_data, start_offset); output_buffer.AppendData(payload_data, start_offset);
absl::optional<SpsParser::SpsState> sps; absl::optional<SpsParser::SpsState> sps;
SpsVuiRewriter::ParseResult result = SpsVuiRewriter::ParseAndRewriteSps( SpsVuiRewriter::ParseResult result = SpsVuiRewriter::ParseAndRewriteSps(
&payload_data[start_offset], end_offset - start_offset, &sps, &payload_data[start_offset], end_offset - start_offset, &sps,
nullptr, output_buffer.get(), SpsVuiRewriter::Direction::kIncoming); nullptr, &output_buffer, SpsVuiRewriter::Direction::kIncoming);
if (result == SpsVuiRewriter::ParseResult::kVuiRewritten) { if (result == SpsVuiRewriter::ParseResult::kVuiRewritten) {
if (modified_buffer_) { if (modified_buffer) {
RTC_LOG(LS_WARNING) RTC_LOG(LS_WARNING)
<< "More than one H264 SPS NAL units needing " << "More than one H264 SPS NAL units needing "
"rewriting found within a single STAP-A packet. " "rewriting found within a single STAP-A packet. "
@ -155,27 +154,29 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
start_offset - (H264::kNaluTypeSize + kLengthFieldSize); start_offset - (H264::kNaluTypeSize + kLengthFieldSize);
// Stap-A Length includes payload data and type header. // Stap-A Length includes payload data and type header.
size_t rewritten_size = size_t rewritten_size =
output_buffer->size() - start_offset + H264::kNaluTypeSize; output_buffer.size() - start_offset + H264::kNaluTypeSize;
ByteWriter<uint16_t>::WriteBigEndian( ByteWriter<uint16_t>::WriteBigEndian(
&(*output_buffer)[length_field_offset], rewritten_size); &output_buffer[length_field_offset], rewritten_size);
} }
parsed_payload->video_payload.SetData(output_buffer.data(),
output_buffer.size());
// Append rest of packet. // Append rest of packet.
output_buffer->AppendData(&payload_data[end_offset], parsed_payload->video_payload.AppendData(
nalu_length + kNalHeaderSize - end_offset); &payload_data[end_offset],
nalu_length + kNalHeaderSize - end_offset);
modified_buffer_ = std::move(output_buffer); modified_buffer = true;
length_ = modified_buffer_->size();
} }
if (sps) { if (sps) {
parsed_payload->video_header().width = sps->width; parsed_payload->video_header.width = sps->width;
parsed_payload->video_header().height = sps->height; parsed_payload->video_header.height = sps->height;
nalu.sps_id = sps->id; nalu.sps_id = sps->id;
} else { } else {
RTC_LOG(LS_WARNING) << "Failed to parse SPS id from SPS slice."; RTC_LOG(LS_WARNING) << "Failed to parse SPS id from SPS slice.";
} }
parsed_payload->video_header().frame_type = parsed_payload->video_header.frame_type =
VideoFrameType::kVideoFrameKey; VideoFrameType::kVideoFrameKey;
break; break;
} }
@ -194,7 +195,7 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
break; break;
} }
case H264::NaluType::kIdr: case H264::NaluType::kIdr:
parsed_payload->video_header().frame_type = parsed_payload->video_header.frame_type =
VideoFrameType::kVideoFrameKey; VideoFrameType::kVideoFrameKey;
RTC_FALLTHROUGH(); RTC_FALLTHROUGH();
case H264::NaluType::kSlice: { case H264::NaluType::kSlice: {
@ -218,7 +219,7 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
case H264::NaluType::kStapA: case H264::NaluType::kStapA:
case H264::NaluType::kFuA: case H264::NaluType::kFuA:
RTC_LOG(LS_WARNING) << "Unexpected STAP-A or FU-A received."; RTC_LOG(LS_WARNING) << "Unexpected STAP-A or FU-A received.";
return false; return absl::nullopt;
} }
if (h264_header.nalus_length == kMaxNalusPerPacket) { if (h264_header.nalus_length == kMaxNalusPerPacket) {
@ -230,28 +231,28 @@ bool RtpDepacketizerH264::ProcessStapAOrSingleNalu(
} }
} }
return true; return parsed_payload;
} }
bool RtpDepacketizerH264::ParseFuaNalu( absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> ParseFuaNalu(
RtpDepacketizer::ParsedPayload* parsed_payload, rtc::CopyOnWriteBuffer rtp_payload) {
const uint8_t* payload_data) { if (rtp_payload.size() < kFuAHeaderSize) {
if (length_ < kFuAHeaderSize) {
RTC_LOG(LS_ERROR) << "FU-A NAL units truncated."; RTC_LOG(LS_ERROR) << "FU-A NAL units truncated.";
return false; return absl::nullopt;
} }
uint8_t fnri = payload_data[0] & (kFBit | kNriMask); absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed_payload(
uint8_t original_nal_type = payload_data[1] & kTypeMask; absl::in_place);
bool first_fragment = (payload_data[1] & kSBit) > 0; uint8_t fnri = rtp_payload.cdata()[0] & (kFBit | kNriMask);
uint8_t original_nal_type = rtp_payload.cdata()[1] & kTypeMask;
bool first_fragment = (rtp_payload.cdata()[1] & kSBit) > 0;
NaluInfo nalu; NaluInfo nalu;
nalu.type = original_nal_type; nalu.type = original_nal_type;
nalu.sps_id = -1; nalu.sps_id = -1;
nalu.pps_id = -1; nalu.pps_id = -1;
if (first_fragment) { if (first_fragment) {
offset_ = 0; absl::optional<uint32_t> pps_id =
length_ -= kNalHeaderSize; PpsParser::ParsePpsIdFromSlice(rtp_payload.cdata() + 2 * kNalHeaderSize,
absl::optional<uint32_t> pps_id = PpsParser::ParsePpsIdFromSlice( rtp_payload.size() - 2 * kNalHeaderSize);
payload_data + 2 * kNalHeaderSize, length_ - kNalHeaderSize);
if (pps_id) { if (pps_id) {
nalu.pps_id = *pps_id; nalu.pps_id = *pps_id;
} else { } else {
@ -261,70 +262,55 @@ bool RtpDepacketizerH264::ParseFuaNalu(
<< static_cast<int>(nalu.type); << static_cast<int>(nalu.type);
} }
uint8_t original_nal_header = fnri | original_nal_type; uint8_t original_nal_header = fnri | original_nal_type;
modified_buffer_.reset(new rtc::Buffer()); rtp_payload =
modified_buffer_->AppendData(payload_data + kNalHeaderSize, length_); rtp_payload.Slice(kNalHeaderSize, rtp_payload.size() - kNalHeaderSize);
(*modified_buffer_)[0] = original_nal_header; rtp_payload[0] = original_nal_header;
parsed_payload->video_payload = std::move(rtp_payload);
} else { } else {
offset_ = kFuAHeaderSize; parsed_payload->video_payload =
length_ -= kFuAHeaderSize; rtp_payload.Slice(kFuAHeaderSize, rtp_payload.size() - kFuAHeaderSize);
} }
if (original_nal_type == H264::NaluType::kIdr) { if (original_nal_type == H264::NaluType::kIdr) {
parsed_payload->video_header().frame_type = VideoFrameType::kVideoFrameKey; parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameKey;
} else { } else {
parsed_payload->video_header().frame_type = parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameDelta;
VideoFrameType::kVideoFrameDelta;
} }
parsed_payload->video_header().width = 0; parsed_payload->video_header.width = 0;
parsed_payload->video_header().height = 0; parsed_payload->video_header.height = 0;
parsed_payload->video_header().codec = kVideoCodecH264; parsed_payload->video_header.codec = kVideoCodecH264;
parsed_payload->video_header().simulcastIdx = 0; parsed_payload->video_header.simulcastIdx = 0;
parsed_payload->video_header().is_first_packet_in_frame = first_fragment; parsed_payload->video_header.is_first_packet_in_frame = first_fragment;
auto& h264_header = absl::get<RTPVideoHeaderH264>( auto& h264_header = parsed_payload->video_header.video_type_header
parsed_payload->video_header().video_type_header); .emplace<RTPVideoHeaderH264>();
h264_header.packetization_type = kH264FuA; h264_header.packetization_type = kH264FuA;
h264_header.nalu_type = original_nal_type; h264_header.nalu_type = original_nal_type;
if (first_fragment) { if (first_fragment) {
h264_header.nalus[h264_header.nalus_length] = nalu; h264_header.nalus[h264_header.nalus_length] = nalu;
h264_header.nalus_length = 1; h264_header.nalus_length = 1;
} }
return true; return parsed_payload;
} }
bool RtpDepacketizerH264::Parse(ParsedPayload* parsed_payload, } // namespace
const uint8_t* payload_data,
size_t payload_data_length) { absl::optional<VideoRtpDepacketizer::ParsedRtpPayload>
RTC_CHECK(parsed_payload != nullptr); VideoRtpDepacketizerH264::Parse(rtc::CopyOnWriteBuffer rtp_payload) {
if (payload_data_length == 0) { if (rtp_payload.size() == 0) {
RTC_LOG(LS_ERROR) << "Empty payload."; RTC_LOG(LS_ERROR) << "Empty payload.";
return false; return absl::nullopt;
} }
offset_ = 0; uint8_t nal_type = rtp_payload.cdata()[0] & kTypeMask;
length_ = payload_data_length;
modified_buffer_.reset();
uint8_t nal_type = payload_data[0] & kTypeMask;
parsed_payload->video_header()
.video_type_header.emplace<RTPVideoHeaderH264>();
if (nal_type == H264::NaluType::kFuA) { if (nal_type == H264::NaluType::kFuA) {
// Fragmented NAL units (FU-A). // Fragmented NAL units (FU-A).
if (!ParseFuaNalu(parsed_payload, payload_data)) return ParseFuaNalu(std::move(rtp_payload));
return false;
} else { } else {
// We handle STAP-A and single NALU's the same way here. The jitter buffer // We handle STAP-A and single NALU's the same way here. The jitter buffer
// will depacketize the STAP-A into NAL units later. // will depacketize the STAP-A into NAL units later.
// TODO(sprang): Parse STAP-A offsets here and store in fragmentation vec. return ProcessStapAOrSingleNalu(std::move(rtp_payload));
if (!ProcessStapAOrSingleNalu(parsed_payload, payload_data))
return false;
} }
const uint8_t* payload =
modified_buffer_ ? modified_buffer_->data() : payload_data;
parsed_payload->payload = payload + offset_;
parsed_payload->payload_length = length_;
return true;
} }
} // namespace webrtc } // namespace webrtc

View File

@ -11,33 +11,17 @@
#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_ #ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
#define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_ #define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
#include <stddef.h> #include "absl/types/optional.h"
#include <stdint.h> #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/copy_on_write_buffer.h"
#include <memory>
#include "modules/rtp_rtcp/source/rtp_format.h"
#include "rtc_base/buffer.h"
namespace webrtc { namespace webrtc {
class RtpDepacketizerH264 : public RtpDepacketizer { class VideoRtpDepacketizerH264 : public VideoRtpDepacketizer {
public: public:
RtpDepacketizerH264(); ~VideoRtpDepacketizerH264() override = default;
~RtpDepacketizerH264() override;
bool Parse(ParsedPayload* parsed_payload, absl::optional<ParsedRtpPayload> Parse(
const uint8_t* payload_data, rtc::CopyOnWriteBuffer rtp_payload) override;
size_t payload_data_length) override;
private:
bool ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
const uint8_t* payload_data);
bool ProcessStapAOrSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload,
const uint8_t* payload_data);
size_t offset_;
size_t length_;
std::unique_ptr<rtc::Buffer> modified_buffer_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -10,14 +10,16 @@
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include <memory> #include <cstdint>
#include <vector> #include <vector>
#include "absl/types/optional.h"
#include "api/array_view.h" #include "api/array_view.h"
#include "common_video/h264/h264_common.h" #include "common_video/h264/h264_common.h"
#include "modules/include/module_common_types.h" #include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
#include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/gmock.h" #include "test/gmock.h"
#include "test/gtest.h" #include "test/gtest.h"
@ -47,67 +49,56 @@ enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F };
// Bit masks for FU (A and B) headers. // Bit masks for FU (A and B) headers.
enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
const uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, constexpr uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xC0}; 0xF4, 0x05, 0x03, 0xC7, 0xC0};
const uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, 0xF4, 0x05, 0x03, constexpr uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
0xC7, 0xE0, 0x1B, 0x41, 0x10, 0x8D, 0x00}; 0xF4, 0x05, 0x03, 0xC7, 0xE0,
const uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04}; 0x1B, 0x41, 0x10, 0x8D, 0x00};
const uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11}; constexpr uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04};
constexpr uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11};
struct H264ParsedPayload : public RtpDepacketizer::ParsedPayload { TEST(VideoRtpDepacketizerH264Test, SingleNalu) {
RTPVideoHeaderH264& h264() {
return absl::get<RTPVideoHeaderH264>(video.video_type_header);
}
};
class RtpDepacketizerH264Test : public ::testing::Test {
protected:
RtpDepacketizerH264Test()
: depacketizer_(std::make_unique<RtpDepacketizerH264>()) {}
void ExpectPacket(H264ParsedPayload* parsed_payload,
const uint8_t* data,
size_t length) {
ASSERT_TRUE(parsed_payload != NULL);
EXPECT_THAT(std::vector<uint8_t>(
parsed_payload->payload,
parsed_payload->payload + parsed_payload->payload_length),
::testing::ElementsAreArray(data, length));
}
std::unique_ptr<RtpDepacketizer> depacketizer_;
};
TEST_F(RtpDepacketizerH264Test, TestSingleNalu) {
uint8_t packet[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5 (IDR). uint8_t packet[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5 (IDR).
H264ParsedPayload payload; rtc::CopyOnWriteBuffer rtp_payload(packet);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); VideoRtpDepacketizerH264 depacketizer;
ExpectPacket(&payload, packet, sizeof(packet)); absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); depacketizer.Parse(rtp_payload);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); ASSERT_TRUE(parsed);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264SingleNalu, payload.h264().packetization_type); EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(kIdr, payload.h264().nalu_type); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
EXPECT_EQ(h264.nalu_type, kIdr);
} }
TEST_F(RtpDepacketizerH264Test, TestSingleNaluSpsWithResolution) { TEST(VideoRtpDepacketizerH264Test, SingleNaluSpsWithResolution) {
uint8_t packet[] = {kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50, uint8_t packet[] = {kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x50,
0x05, 0xBA, 0x10, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x05, 0xBA, 0x10, 0x00, 0x00, 0x03, 0x00, 0xC0,
0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25}; 0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25};
H264ParsedPayload payload; rtc::CopyOnWriteBuffer rtp_payload(packet);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); VideoRtpDepacketizerH264 depacketizer;
ExpectPacket(&payload, packet, sizeof(packet)); absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); depacketizer.Parse(rtp_payload);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); ASSERT_TRUE(parsed);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264SingleNalu, payload.h264().packetization_type); EXPECT_EQ(parsed->video_payload, rtp_payload);
EXPECT_EQ(1280u, payload.video_header().width); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(720u, payload.video_header().height); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed->video_header.width, 1280u);
EXPECT_EQ(parsed->video_header.height, 720u);
const auto& h264 =
absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
} }
TEST_F(RtpDepacketizerH264Test, TestStapAKey) { TEST(VideoRtpDepacketizerH264Test, StapAKey) {
// clang-format off // clang-format off
const NaluInfo kExpectedNalus[] = { {H264::kSps, 0, -1}, const NaluInfo kExpectedNalus[] = { {H264::kSps, 0, -1},
{H264::kPps, 1, 2}, {H264::kPps, 1, 2},
@ -124,29 +115,34 @@ TEST_F(RtpDepacketizerH264Test, TestStapAKey) {
0, 0xB, kExpectedNalus[2].type, 0, 0xB, kExpectedNalus[2].type,
0x85, 0xB8, 0x0, 0x4, 0x0, 0x0, 0x13, 0x93, 0x12, 0x0}; 0x85, 0xB8, 0x0, 0x4, 0x0, 0x0, 0x13, 0x93, 0x12, 0x0};
// clang-format on // clang-format on
rtc::CopyOnWriteBuffer rtp_payload(packet);
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
ExpectPacket(&payload, packet, sizeof(packet)); depacketizer.Parse(rtp_payload);
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); ASSERT_TRUE(parsed);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed->video_payload, rtp_payload);
const RTPVideoHeaderH264& h264 = payload.h264(); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(kH264StapA, h264.packetization_type); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const auto& h264 =
absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264StapA);
// NALU type for aggregated packets is the type of the first packet only. // NALU type for aggregated packets is the type of the first packet only.
EXPECT_EQ(kSps, h264.nalu_type); EXPECT_EQ(h264.nalu_type, kSps);
ASSERT_EQ(3u, h264.nalus_length); ASSERT_EQ(h264.nalus_length, 3u);
for (size_t i = 0; i < h264.nalus_length; ++i) { for (size_t i = 0; i < h264.nalus_length; ++i) {
EXPECT_EQ(kExpectedNalus[i].type, h264.nalus[i].type) EXPECT_EQ(h264.nalus[i].type, kExpectedNalus[i].type)
<< "Failed parsing nalu " << i; << "Failed parsing nalu " << i;
EXPECT_EQ(kExpectedNalus[i].sps_id, h264.nalus[i].sps_id) EXPECT_EQ(h264.nalus[i].sps_id, kExpectedNalus[i].sps_id)
<< "Failed parsing nalu " << i; << "Failed parsing nalu " << i;
EXPECT_EQ(kExpectedNalus[i].pps_id, h264.nalus[i].pps_id) EXPECT_EQ(h264.nalus[i].pps_id, kExpectedNalus[i].pps_id)
<< "Failed parsing nalu " << i; << "Failed parsing nalu " << i;
} }
} }
TEST_F(RtpDepacketizerH264Test, TestStapANaluSpsWithResolution) { TEST(VideoRtpDepacketizerH264Test, StapANaluSpsWithResolution) {
uint8_t packet[] = {kStapA, // F=0, NRI=0, Type=24. uint8_t packet[] = {kStapA, // F=0, NRI=0, Type=24.
// Length (2 bytes), nal header, payload. // Length (2 bytes), nal header, payload.
0x00, 0x19, kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40, 0x00, 0x19, kSps, 0x7A, 0x00, 0x1F, 0xBC, 0xD9, 0x40,
@ -154,45 +150,44 @@ TEST_F(RtpDepacketizerH264Test, TestStapANaluSpsWithResolution) {
0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25, 0x80, 0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25, 0x80,
0x00, 0x03, kIdr, 0xFF, 0x00, 0x00, 0x04, kIdr, 0xFF, 0x00, 0x03, kIdr, 0xFF, 0x00, 0x00, 0x04, kIdr, 0xFF,
0x00, 0x11}; 0x00, 0x11};
rtc::CopyOnWriteBuffer rtp_payload(packet);
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); EXPECT_EQ(parsed->video_payload, rtp_payload);
ExpectPacket(&payload, packet, sizeof(packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed->video_header.width, 1280u);
EXPECT_EQ(kH264StapA, payload.h264().packetization_type); EXPECT_EQ(parsed->video_header.height, 720u);
EXPECT_EQ(1280u, payload.video_header().width); const auto& h264 =
EXPECT_EQ(720u, payload.video_header().height); absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264StapA);
} }
TEST_F(RtpDepacketizerH264Test, TestEmptyStapARejected) { TEST(VideoRtpDepacketizerH264Test, EmptyStapARejected) {
uint8_t lone_empty_packet[] = {kStapA, 0x00, 0x00}; uint8_t lone_empty_packet[] = {kStapA, 0x00, 0x00};
uint8_t leading_empty_packet[] = {kStapA, 0x00, 0x00, 0x00, 0x04, uint8_t leading_empty_packet[] = {kStapA, 0x00, 0x00, 0x00, 0x04,
kIdr, 0xFF, 0x00, 0x11}; kIdr, 0xFF, 0x00, 0x11};
uint8_t middle_empty_packet[] = {kStapA, 0x00, 0x03, kIdr, 0xFF, 0x00, 0x00, uint8_t middle_empty_packet[] = {kStapA, 0x00, 0x03, kIdr, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x04, kIdr, 0xFF, 0x00, 0x11}; 0x00, 0x00, 0x04, kIdr, 0xFF, 0x00, 0x11};
uint8_t trailing_empty_packet[] = {kStapA, 0x00, 0x03, kIdr, uint8_t trailing_empty_packet[] = {kStapA, 0x00, 0x03, kIdr,
0xFF, 0x00, 0x00, 0x00}; 0xFF, 0x00, 0x00, 0x00};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(lone_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, lone_empty_packet, EXPECT_FALSE(
sizeof(lone_empty_packet))); depacketizer.Parse(rtc::CopyOnWriteBuffer(leading_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, leading_empty_packet, EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(middle_empty_packet)));
sizeof(leading_empty_packet))); EXPECT_FALSE(
EXPECT_FALSE(depacketizer_->Parse(&payload, middle_empty_packet, depacketizer.Parse(rtc::CopyOnWriteBuffer(trailing_empty_packet)));
sizeof(middle_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, trailing_empty_packet,
sizeof(trailing_empty_packet)));
} }
TEST_F(RtpDepacketizerH264Test, DepacketizeWithRewriting) { TEST(VideoRtpDepacketizerH264Test, DepacketizeWithRewriting) {
rtc::Buffer in_buffer; rtc::CopyOnWriteBuffer in_buffer;
rtc::Buffer out_buffer; rtc::Buffer out_buffer;
uint8_t kHeader[2] = {kStapA}; uint8_t kHeader[2] = {kStapA};
@ -218,20 +213,16 @@ TEST_F(RtpDepacketizerH264Test, DepacketizeWithRewriting) {
out_buffer.AppendData(kHeader, 2); out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo); out_buffer.AppendData(kIdrTwo);
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_TRUE( auto parsed = depacketizer.Parse(in_buffer);
depacketizer_->Parse(&payload, in_buffer.data(), in_buffer.size())); ASSERT_TRUE(parsed);
EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
std::vector<uint8_t> expected_packet_payload( parsed->video_payload.size()),
out_buffer.data(), &out_buffer.data()[out_buffer.size()]); ElementsAreArray(out_buffer));
EXPECT_THAT(
expected_packet_payload,
::testing::ElementsAreArray(payload.payload, payload.payload_length));
} }
TEST_F(RtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) { TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
rtc::Buffer in_buffer; rtc::CopyOnWriteBuffer in_buffer;
rtc::Buffer out_buffer; rtc::Buffer out_buffer;
uint8_t kHeader[2] = {kStapA}; uint8_t kHeader[2] = {kStapA};
@ -265,37 +256,42 @@ TEST_F(RtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
out_buffer.AppendData(kHeader, 2); out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo); out_buffer.AppendData(kIdrTwo);
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_TRUE( auto parsed = depacketizer.Parse(in_buffer);
depacketizer_->Parse(&payload, in_buffer.data(), in_buffer.size())); ASSERT_TRUE(parsed);
std::vector<uint8_t> expected_packet_payload( std::vector<uint8_t> expected_packet_payload(
out_buffer.data(), &out_buffer.data()[out_buffer.size()]); out_buffer.data(), &out_buffer.data()[out_buffer.size()]);
EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
EXPECT_THAT( parsed->video_payload.size()),
expected_packet_payload, ElementsAreArray(out_buffer));
::testing::ElementsAreArray(payload.payload, payload.payload_length));
} }
TEST_F(RtpDepacketizerH264Test, TestStapADelta) { TEST(VideoRtpDepacketizerH264Test, StapADelta) {
uint8_t packet[16] = {kStapA, // F=0, NRI=0, Type=24. uint8_t packet[16] = {kStapA, // F=0, NRI=0, Type=24.
// Length, nal header, payload. // Length, nal header, payload.
0, 0x02, kSlice, 0xFF, 0, 0x03, kSlice, 0xFF, 0x00, 0, 0, 0x02, kSlice, 0xFF, 0, 0x03, kSlice, 0xFF, 0x00, 0,
0x04, kSlice, 0xFF, 0x00, 0x11}; 0x04, kSlice, 0xFF, 0x00, 0x11};
H264ParsedPayload payload; rtc::CopyOnWriteBuffer rtp_payload(packet);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet))); VideoRtpDepacketizerH264 depacketizer;
ExpectPacket(&payload, packet, sizeof(packet)); absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
EXPECT_EQ(VideoFrameType::kVideoFrameDelta, depacketizer.Parse(rtp_payload);
payload.video_header().frame_type); ASSERT_TRUE(parsed);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size());
EXPECT_EQ(kH264StapA, payload.h264().packetization_type); EXPECT_EQ(parsed->video_payload.cdata(), rtp_payload.cdata());
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(parsed->video_header.codec, kVideoCodecH264);
EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264StapA);
// NALU type for aggregated packets is the type of the first packet only. // NALU type for aggregated packets is the type of the first packet only.
EXPECT_EQ(kSlice, payload.h264().nalu_type); EXPECT_EQ(h264.nalu_type, kSlice);
} }
TEST_F(RtpDepacketizerH264Test, TestFuA) { TEST(VideoRtpDepacketizerH264Test, FuA) {
// clang-format off // clang-format off
uint8_t packet1[] = { uint8_t packet1[] = {
kFuA, // F=0, NRI=0, Type=28. kFuA, // F=0, NRI=0, Type=28.
@ -320,107 +316,115 @@ TEST_F(RtpDepacketizerH264Test, TestFuA) {
}; };
const uint8_t kExpected3[] = {0x03}; const uint8_t kExpected3[] = {0x03};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
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 // We expect that the first packet is one byte shorter since the FU-A header
// has been replaced by the original nal header. // has been replaced by the original nal header.
ASSERT_TRUE(depacketizer_->Parse(&payload, packet1, sizeof(packet1))); EXPECT_THAT(rtc::MakeArrayView(parsed1->video_payload.cdata(),
ExpectPacket(&payload, kExpected1, sizeof(kExpected1)); parsed1->video_payload.size()),
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); ElementsAreArray(kExpected1));
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH264);
const RTPVideoHeaderH264& h264 = payload.h264(); EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame);
EXPECT_EQ(kH264FuA, h264.packetization_type); {
EXPECT_EQ(kIdr, h264.nalu_type); const RTPVideoHeaderH264& h264 =
ASSERT_EQ(1u, h264.nalus_length); absl::get<RTPVideoHeaderH264>(parsed1->video_header.video_type_header);
EXPECT_EQ(static_cast<H264::NaluType>(kIdr), h264.nalus[0].type); EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(-1, h264.nalus[0].sps_id); EXPECT_EQ(h264.nalu_type, kIdr);
EXPECT_EQ(0, h264.nalus[0].pps_id); ASSERT_EQ(h264.nalus_length, 1u);
EXPECT_EQ(h264.nalus[0].type, static_cast<H264::NaluType>(kIdr));
EXPECT_EQ(h264.nalus[0].sps_id, -1);
EXPECT_EQ(h264.nalus[0].pps_id, 0);
}
// Following packets will be 2 bytes shorter since they will only be appended // Following packets will be 2 bytes shorter since they will only be appended
// onto the first packet. // onto the first packet.
payload = H264ParsedPayload(); auto parsed2 = depacketizer.Parse(rtc::CopyOnWriteBuffer(packet2));
ASSERT_TRUE(depacketizer_->Parse(&payload, packet2, sizeof(packet2))); EXPECT_THAT(rtc::MakeArrayView(parsed2->video_payload.cdata(),
ExpectPacket(&payload, kExpected2, sizeof(kExpected2)); parsed2->video_payload.size()),
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); ElementsAreArray(kExpected2));
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame);
EXPECT_FALSE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH264);
{ {
const RTPVideoHeaderH264& h264 = payload.h264(); const RTPVideoHeaderH264& h264 =
EXPECT_EQ(kH264FuA, h264.packetization_type); absl::get<RTPVideoHeaderH264>(parsed2->video_header.video_type_header);
EXPECT_EQ(kIdr, h264.nalu_type); EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(h264.nalu_type, kIdr);
// NALU info is only expected for the first FU-A packet. // NALU info is only expected for the first FU-A packet.
EXPECT_EQ(0u, h264.nalus_length); EXPECT_EQ(h264.nalus_length, 0u);
} }
payload = H264ParsedPayload(); auto parsed3 = depacketizer.Parse(rtc::CopyOnWriteBuffer(packet3));
ASSERT_TRUE(depacketizer_->Parse(&payload, packet3, sizeof(packet3))); EXPECT_THAT(rtc::MakeArrayView(parsed3->video_payload.cdata(),
ExpectPacket(&payload, kExpected3, sizeof(kExpected3)); parsed3->video_payload.size()),
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type); ElementsAreArray(kExpected3));
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec); EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame);
EXPECT_FALSE(payload.video_header().is_first_packet_in_frame); EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH264);
{ {
const RTPVideoHeaderH264& h264 = payload.h264(); const RTPVideoHeaderH264& h264 =
EXPECT_EQ(kH264FuA, h264.packetization_type); absl::get<RTPVideoHeaderH264>(parsed3->video_header.video_type_header);
EXPECT_EQ(kIdr, h264.nalu_type); EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(h264.nalu_type, kIdr);
// NALU info is only expected for the first FU-A packet. // NALU info is only expected for the first FU-A packet.
ASSERT_EQ(0u, h264.nalus_length); ASSERT_EQ(h264.nalus_length, 0u);
} }
} }
TEST_F(RtpDepacketizerH264Test, TestEmptyPayload) { TEST(VideoRtpDepacketizerH264Test, EmptyPayload) {
// Using a wild pointer to crash on accesses from inside the depacketizer. rtc::CopyOnWriteBuffer empty;
uint8_t* garbage_ptr = reinterpret_cast<uint8_t*>(0x4711); VideoRtpDepacketizerH264 depacketizer;
H264ParsedPayload payload; EXPECT_FALSE(depacketizer.Parse(empty));
EXPECT_FALSE(depacketizer_->Parse(&payload, garbage_ptr, 0));
} }
TEST_F(RtpDepacketizerH264Test, TestTruncatedFuaNalu) { TEST(VideoRtpDepacketizerH264Test, TruncatedFuaNalu) {
const uint8_t kPayload[] = {0x9c}; const uint8_t kPayload[] = {0x9c};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
} }
TEST_F(RtpDepacketizerH264Test, TestTruncatedSingleStapANalu) { TEST(VideoRtpDepacketizerH264Test, TruncatedSingleStapANalu) {
const uint8_t kPayload[] = {0xd8, 0x27}; const uint8_t kPayload[] = {0xd8, 0x27};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
} }
TEST_F(RtpDepacketizerH264Test, TestStapAPacketWithTruncatedNalUnits) { TEST(VideoRtpDepacketizerH264Test, StapAPacketWithTruncatedNalUnits) {
const uint8_t kPayload[] = {0x58, 0xCB, 0xED, 0xDF}; const uint8_t kPayload[] = {0x58, 0xCB, 0xED, 0xDF};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
} }
TEST_F(RtpDepacketizerH264Test, TestTruncationJustAfterSingleStapANalu) { TEST(VideoRtpDepacketizerH264Test, TruncationJustAfterSingleStapANalu) {
const uint8_t kPayload[] = {0x38, 0x27, 0x27}; const uint8_t kPayload[] = {0x38, 0x27, 0x27};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
} }
TEST_F(RtpDepacketizerH264Test, TestShortSpsPacket) { TEST(VideoRtpDepacketizerH264Test, ShortSpsPacket) {
const uint8_t kPayload[] = {0x27, 0x80, 0x00}; const uint8_t kPayload[] = {0x27, 0x80, 0x00};
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
EXPECT_TRUE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); EXPECT_TRUE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
} }
TEST_F(RtpDepacketizerH264Test, TestSeiPacket) { TEST(VideoRtpDepacketizerH264Test, SeiPacket) {
const uint8_t kPayload[] = { const uint8_t kPayload[] = {
kSei, // F=0, NRI=0, Type=6. kSei, // F=0, NRI=0, Type=6.
0x03, 0x03, 0x03, 0x03 // Payload. 0x03, 0x03, 0x03, 0x03 // Payload.
}; };
H264ParsedPayload payload; VideoRtpDepacketizerH264 depacketizer;
ASSERT_TRUE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); auto parsed = depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
const RTPVideoHeaderH264& h264 = payload.h264(); ASSERT_TRUE(parsed);
EXPECT_EQ(VideoFrameType::kVideoFrameDelta, const RTPVideoHeaderH264& h264 =
payload.video_header().frame_type); absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(kH264SingleNalu, h264.packetization_type); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(kSei, h264.nalu_type); EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
ASSERT_EQ(1u, h264.nalus_length); EXPECT_EQ(h264.nalu_type, kSei);
EXPECT_EQ(static_cast<H264::NaluType>(kSei), h264.nalus[0].type); ASSERT_EQ(h264.nalus_length, 1u);
EXPECT_EQ(-1, h264.nalus[0].sps_id); EXPECT_EQ(h264.nalus[0].type, static_cast<H264::NaluType>(kSei));
EXPECT_EQ(-1, h264.nalus[0].pps_id); EXPECT_EQ(h264.nalus[0].sps_id, -1);
EXPECT_EQ(h264.nalus[0].pps_id, -1);
} }
} // namespace } // namespace

View File

@ -7,14 +7,13 @@
* in the file PATENTS. All contributing project authors may * in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include "modules/rtp_rtcp/source/rtp_format_h264.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
namespace webrtc { namespace webrtc {
void FuzzOneInput(const uint8_t* data, size_t size) { void FuzzOneInput(const uint8_t* data, size_t size) {
if (size > 200000) if (size > 200000)
return; return;
RtpDepacketizerH264 depacketizer; VideoRtpDepacketizerH264 depacketizer;
RtpDepacketizer::ParsedPayload parsed_payload; depacketizer.Parse(rtc::CopyOnWriteBuffer(data, size));
depacketizer.Parse(&parsed_payload, data, size);
} }
} // namespace webrtc } // namespace webrtc