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 "absl/memory/memory.h"
#include "absl/types/optional.h"
#include "api/video/video_codec_type.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_generic.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_vp9.h"
#include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
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(
VideoCodecType codec) {
switch (codec) {
case kVideoCodecH264:
return std::make_unique<Legacy<RtpDepacketizerH264>>();
return std::make_unique<VideoRtpDepacketizerH264>();
case kVideoCodecVP8:
return std::make_unique<VideoRtpDepacketizerVp8>();
case kVideoCodecVP9:

View File

@ -22,7 +22,6 @@
#include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/source/rtp_format.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 "rtc_base/buffer.h"
#include "rtc_base/constructor_magic.h"

View File

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

View File

@ -11,33 +11,17 @@
#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
#define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_H264_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "modules/rtp_rtcp/source/rtp_format.h"
#include "rtc_base/buffer.h"
#include "absl/types/optional.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "rtc_base/copy_on_write_buffer.h"
namespace webrtc {
class RtpDepacketizerH264 : public RtpDepacketizer {
class VideoRtpDepacketizerH264 : public VideoRtpDepacketizer {
public:
RtpDepacketizerH264();
~RtpDepacketizerH264() override;
~VideoRtpDepacketizerH264() override = default;
bool Parse(ParsedPayload* parsed_payload,
const uint8_t* payload_data,
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_;
absl::optional<ParsedRtpPayload> Parse(
rtc::CopyOnWriteBuffer rtp_payload) override;
};
} // namespace webrtc

View File

@ -10,14 +10,16 @@
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include <memory>
#include <cstdint>
#include <vector>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "common_video/h264/h264_common.h"
#include "modules/include/module_common_types.h"
#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/gmock.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.
enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
const uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xC0};
const uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, 0xF4, 0x05, 0x03,
0xC7, 0xE0, 0x1B, 0x41, 0x10, 0x8D, 0x00};
const uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04};
const uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11};
constexpr uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xC0};
constexpr uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03,
0xF4, 0x05, 0x03, 0xC7, 0xE0,
0x1B, 0x41, 0x10, 0x8D, 0x00};
constexpr uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04};
constexpr uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11};
struct H264ParsedPayload : public RtpDepacketizer::ParsedPayload {
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) {
TEST(VideoRtpDepacketizerH264Test, SingleNalu) {
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)));
ExpectPacket(&payload, packet, sizeof(packet));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264SingleNalu, payload.h264().packetization_type);
EXPECT_EQ(kIdr, payload.h264().nalu_type);
VideoRtpDepacketizerH264 depacketizer;
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
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,
0x05, 0xBA, 0x10, 0x00, 0x00, 0x03, 0x00, 0xC0,
0x00, 0x00, 0x03, 0x2A, 0xE0, 0xF1, 0x83, 0x25};
H264ParsedPayload payload;
rtc::CopyOnWriteBuffer rtp_payload(packet);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet)));
ExpectPacket(&payload, packet, sizeof(packet));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264SingleNalu, payload.h264().packetization_type);
EXPECT_EQ(1280u, payload.video_header().width);
EXPECT_EQ(720u, payload.video_header().height);
VideoRtpDepacketizerH264 depacketizer;
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
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);
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
const NaluInfo kExpectedNalus[] = { {H264::kSps, 0, -1},
{H264::kPps, 1, 2},
@ -124,29 +115,34 @@ TEST_F(RtpDepacketizerH264Test, TestStapAKey) {
0, 0xB, kExpectedNalus[2].type,
0x85, 0xB8, 0x0, 0x4, 0x0, 0x0, 0x13, 0x93, 0x12, 0x0};
// clang-format on
rtc::CopyOnWriteBuffer rtp_payload(packet);
H264ParsedPayload payload;
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet)));
ExpectPacket(&payload, packet, sizeof(packet));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 = payload.h264();
EXPECT_EQ(kH264StapA, h264.packetization_type);
VideoRtpDepacketizerH264 depacketizer;
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload, rtp_payload);
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 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.
EXPECT_EQ(kSps, h264.nalu_type);
ASSERT_EQ(3u, h264.nalus_length);
EXPECT_EQ(h264.nalu_type, kSps);
ASSERT_EQ(h264.nalus_length, 3u);
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;
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;
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;
}
}
TEST_F(RtpDepacketizerH264Test, TestStapANaluSpsWithResolution) {
TEST(VideoRtpDepacketizerH264Test, StapANaluSpsWithResolution) {
uint8_t packet[] = {kStapA, // F=0, NRI=0, Type=24.
// Length (2 bytes), nal header, payload.
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, 0x03, kIdr, 0xFF, 0x00, 0x00, 0x04, kIdr, 0xFF,
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)));
ExpectPacket(&payload, packet, sizeof(packet));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264StapA, payload.h264().packetization_type);
EXPECT_EQ(1280u, payload.video_header().width);
EXPECT_EQ(720u, payload.video_header().height);
EXPECT_EQ(parsed->video_payload, rtp_payload);
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);
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, kH264StapA);
}
TEST_F(RtpDepacketizerH264Test, TestEmptyStapARejected) {
TEST(VideoRtpDepacketizerH264Test, EmptyStapARejected) {
uint8_t lone_empty_packet[] = {kStapA, 0x00, 0x00};
uint8_t leading_empty_packet[] = {kStapA, 0x00, 0x00, 0x00, 0x04,
kIdr, 0xFF, 0x00, 0x11};
uint8_t middle_empty_packet[] = {kStapA, 0x00, 0x03, kIdr, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x04, kIdr, 0xFF, 0x00, 0x11};
uint8_t trailing_empty_packet[] = {kStapA, 0x00, 0x03, kIdr,
0xFF, 0x00, 0x00, 0x00};
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, lone_empty_packet,
sizeof(lone_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, leading_empty_packet,
sizeof(leading_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, middle_empty_packet,
sizeof(middle_empty_packet)));
EXPECT_FALSE(depacketizer_->Parse(&payload, trailing_empty_packet,
sizeof(trailing_empty_packet)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(lone_empty_packet)));
EXPECT_FALSE(
depacketizer.Parse(rtc::CopyOnWriteBuffer(leading_empty_packet)));
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(middle_empty_packet)));
EXPECT_FALSE(
depacketizer.Parse(rtc::CopyOnWriteBuffer(trailing_empty_packet)));
}
TEST_F(RtpDepacketizerH264Test, DepacketizeWithRewriting) {
rtc::Buffer in_buffer;
TEST(VideoRtpDepacketizerH264Test, DepacketizeWithRewriting) {
rtc::CopyOnWriteBuffer in_buffer;
rtc::Buffer out_buffer;
uint8_t kHeader[2] = {kStapA};
@ -218,20 +213,16 @@ TEST_F(RtpDepacketizerH264Test, DepacketizeWithRewriting) {
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo);
H264ParsedPayload payload;
EXPECT_TRUE(
depacketizer_->Parse(&payload, in_buffer.data(), in_buffer.size()));
std::vector<uint8_t> expected_packet_payload(
out_buffer.data(), &out_buffer.data()[out_buffer.size()]);
EXPECT_THAT(
expected_packet_payload,
::testing::ElementsAreArray(payload.payload, payload.payload_length));
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(in_buffer);
ASSERT_TRUE(parsed);
EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
parsed->video_payload.size()),
ElementsAreArray(out_buffer));
}
TEST_F(RtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
rtc::Buffer in_buffer;
TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
rtc::CopyOnWriteBuffer in_buffer;
rtc::Buffer out_buffer;
uint8_t kHeader[2] = {kStapA};
@ -265,37 +256,42 @@ TEST_F(RtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) {
out_buffer.AppendData(kHeader, 2);
out_buffer.AppendData(kIdrTwo);
H264ParsedPayload payload;
EXPECT_TRUE(
depacketizer_->Parse(&payload, in_buffer.data(), in_buffer.size()));
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(in_buffer);
ASSERT_TRUE(parsed);
std::vector<uint8_t> expected_packet_payload(
out_buffer.data(), &out_buffer.data()[out_buffer.size()]);
EXPECT_THAT(
expected_packet_payload,
::testing::ElementsAreArray(payload.payload, payload.payload_length));
EXPECT_THAT(rtc::MakeArrayView(parsed->video_payload.cdata(),
parsed->video_payload.size()),
ElementsAreArray(out_buffer));
}
TEST_F(RtpDepacketizerH264Test, TestStapADelta) {
TEST(VideoRtpDepacketizerH264Test, StapADelta) {
uint8_t packet[16] = {kStapA, // F=0, NRI=0, Type=24.
// Length, nal header, payload.
0, 0x02, kSlice, 0xFF, 0, 0x03, kSlice, 0xFF, 0x00, 0,
0x04, kSlice, 0xFF, 0x00, 0x11};
H264ParsedPayload payload;
rtc::CopyOnWriteBuffer rtp_payload(packet);
ASSERT_TRUE(depacketizer_->Parse(&payload, packet, sizeof(packet)));
ExpectPacket(&payload, packet, sizeof(packet));
EXPECT_EQ(VideoFrameType::kVideoFrameDelta,
payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
EXPECT_EQ(kH264StapA, payload.h264().packetization_type);
VideoRtpDepacketizerH264 depacketizer;
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
depacketizer.Parse(rtp_payload);
ASSERT_TRUE(parsed);
EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size());
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.
EXPECT_EQ(kSlice, payload.h264().nalu_type);
EXPECT_EQ(h264.nalu_type, kSlice);
}
TEST_F(RtpDepacketizerH264Test, TestFuA) {
TEST(VideoRtpDepacketizerH264Test, FuA) {
// clang-format off
uint8_t packet1[] = {
kFuA, // F=0, NRI=0, Type=28.
@ -320,107 +316,115 @@ TEST_F(RtpDepacketizerH264Test, TestFuA) {
};
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
// has been replaced by the original nal header.
ASSERT_TRUE(depacketizer_->Parse(&payload, packet1, sizeof(packet1)));
ExpectPacket(&payload, kExpected1, sizeof(kExpected1));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_TRUE(payload.video_header().is_first_packet_in_frame);
const RTPVideoHeaderH264& h264 = payload.h264();
EXPECT_EQ(kH264FuA, h264.packetization_type);
EXPECT_EQ(kIdr, h264.nalu_type);
ASSERT_EQ(1u, h264.nalus_length);
EXPECT_EQ(static_cast<H264::NaluType>(kIdr), h264.nalus[0].type);
EXPECT_EQ(-1, h264.nalus[0].sps_id);
EXPECT_EQ(0, h264.nalus[0].pps_id);
EXPECT_THAT(rtc::MakeArrayView(parsed1->video_payload.cdata(),
parsed1->video_payload.size()),
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);
{
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed1->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(h264.nalu_type, kIdr);
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
// onto the first packet.
payload = H264ParsedPayload();
ASSERT_TRUE(depacketizer_->Parse(&payload, packet2, sizeof(packet2)));
ExpectPacket(&payload, kExpected2, sizeof(kExpected2));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_FALSE(payload.video_header().is_first_packet_in_frame);
auto parsed2 = depacketizer.Parse(rtc::CopyOnWriteBuffer(packet2));
EXPECT_THAT(rtc::MakeArrayView(parsed2->video_payload.cdata(),
parsed2->video_payload.size()),
ElementsAreArray(kExpected2));
EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH264);
{
const RTPVideoHeaderH264& h264 = payload.h264();
EXPECT_EQ(kH264FuA, h264.packetization_type);
EXPECT_EQ(kIdr, h264.nalu_type);
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed2->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(h264.nalu_type, kIdr);
// 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();
ASSERT_TRUE(depacketizer_->Parse(&payload, packet3, sizeof(packet3)));
ExpectPacket(&payload, kExpected3, sizeof(kExpected3));
EXPECT_EQ(VideoFrameType::kVideoFrameKey, payload.video_header().frame_type);
EXPECT_EQ(kVideoCodecH264, payload.video_header().codec);
EXPECT_FALSE(payload.video_header().is_first_packet_in_frame);
auto parsed3 = depacketizer.Parse(rtc::CopyOnWriteBuffer(packet3));
EXPECT_THAT(rtc::MakeArrayView(parsed3->video_payload.cdata(),
parsed3->video_payload.size()),
ElementsAreArray(kExpected3));
EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame);
EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH264);
{
const RTPVideoHeaderH264& h264 = payload.h264();
EXPECT_EQ(kH264FuA, h264.packetization_type);
EXPECT_EQ(kIdr, h264.nalu_type);
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed3->video_header.video_type_header);
EXPECT_EQ(h264.packetization_type, kH264FuA);
EXPECT_EQ(h264.nalu_type, kIdr);
// 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) {
// Using a wild pointer to crash on accesses from inside the depacketizer.
uint8_t* garbage_ptr = reinterpret_cast<uint8_t*>(0x4711);
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, garbage_ptr, 0));
TEST(VideoRtpDepacketizerH264Test, EmptyPayload) {
rtc::CopyOnWriteBuffer empty;
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(empty));
}
TEST_F(RtpDepacketizerH264Test, TestTruncatedFuaNalu) {
TEST(VideoRtpDepacketizerH264Test, TruncatedFuaNalu) {
const uint8_t kPayload[] = {0x9c};
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}
TEST_F(RtpDepacketizerH264Test, TestTruncatedSingleStapANalu) {
TEST(VideoRtpDepacketizerH264Test, TruncatedSingleStapANalu) {
const uint8_t kPayload[] = {0xd8, 0x27};
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}
TEST_F(RtpDepacketizerH264Test, TestStapAPacketWithTruncatedNalUnits) {
TEST(VideoRtpDepacketizerH264Test, StapAPacketWithTruncatedNalUnits) {
const uint8_t kPayload[] = {0x58, 0xCB, 0xED, 0xDF};
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}
TEST_F(RtpDepacketizerH264Test, TestTruncationJustAfterSingleStapANalu) {
TEST(VideoRtpDepacketizerH264Test, TruncationJustAfterSingleStapANalu) {
const uint8_t kPayload[] = {0x38, 0x27, 0x27};
H264ParsedPayload payload;
EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}
TEST_F(RtpDepacketizerH264Test, TestShortSpsPacket) {
TEST(VideoRtpDepacketizerH264Test, ShortSpsPacket) {
const uint8_t kPayload[] = {0x27, 0x80, 0x00};
H264ParsedPayload payload;
EXPECT_TRUE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
VideoRtpDepacketizerH264 depacketizer;
EXPECT_TRUE(depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload)));
}
TEST_F(RtpDepacketizerH264Test, TestSeiPacket) {
TEST(VideoRtpDepacketizerH264Test, SeiPacket) {
const uint8_t kPayload[] = {
kSei, // F=0, NRI=0, Type=6.
0x03, 0x03, 0x03, 0x03 // Payload.
};
H264ParsedPayload payload;
ASSERT_TRUE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload)));
const RTPVideoHeaderH264& h264 = payload.h264();
EXPECT_EQ(VideoFrameType::kVideoFrameDelta,
payload.video_header().frame_type);
EXPECT_EQ(kH264SingleNalu, h264.packetization_type);
EXPECT_EQ(kSei, h264.nalu_type);
ASSERT_EQ(1u, h264.nalus_length);
EXPECT_EQ(static_cast<H264::NaluType>(kSei), h264.nalus[0].type);
EXPECT_EQ(-1, h264.nalus[0].sps_id);
EXPECT_EQ(-1, h264.nalus[0].pps_id);
VideoRtpDepacketizerH264 depacketizer;
auto parsed = depacketizer.Parse(rtc::CopyOnWriteBuffer(kPayload));
ASSERT_TRUE(parsed);
const RTPVideoHeaderH264& h264 =
absl::get<RTPVideoHeaderH264>(parsed->video_header.video_type_header);
EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta);
EXPECT_EQ(h264.packetization_type, kH264SingleNalu);
EXPECT_EQ(h264.nalu_type, kSei);
ASSERT_EQ(h264.nalus_length, 1u);
EXPECT_EQ(h264.nalus[0].type, static_cast<H264::NaluType>(kSei));
EXPECT_EQ(h264.nalus[0].sps_id, -1);
EXPECT_EQ(h264.nalus[0].pps_id, -1);
}
} // namespace

View File

@ -7,14 +7,13 @@
* in the file PATENTS. All contributing project authors may
* 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 {
void FuzzOneInput(const uint8_t* data, size_t size) {
if (size > 200000)
return;
RtpDepacketizerH264 depacketizer;
RtpDepacketizer::ParsedPayload parsed_payload;
depacketizer.Parse(&parsed_payload, data, size);
VideoRtpDepacketizerH264 depacketizer;
depacketizer.Parse(rtc::CopyOnWriteBuffer(data, size));
}
} // namespace webrtc