Skip to content

Commit

Permalink
Add extended header containing frame ID to the generic packetizer.
Browse files Browse the repository at this point in the history
Also changes default value of frame ID in RTPVideoHeader to
kNoPictureId. Special care should be take so that picture ID will not
be set in RTPVideoHeader unless the client on the end supports
deserializing extended generic header.

Bug: webrtc:9582
Change-Id: Ib096373ed187f31e51d481193a2bda56de68f167
Reviewed-on: https://webrtc-review.googlesource.com/92084
Reviewed-by: Danil Chapovalov <[email protected]>
Commit-Queue: Sami Kalliomäki <[email protected]>
Cr-Commit-Position: refs/heads/master@{#24250}
  • Loading branch information
Sami Kalliomäki authored and Commit Bot committed Aug 9, 2018
1 parent 9489c3a commit 426a80c
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 47 deletions.
6 changes: 4 additions & 2 deletions modules/rtp_rtcp/source/nack_rtx_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,10 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
uint32_t timestamp = 3000;
uint16_t nack_list[kVideoNackListSize];
for (int frame = 0; frame < kNumFrames; ++frame) {
RTPVideoHeader video_header;
EXPECT_TRUE(rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp, timestamp / 90,
payload_data, payload_data_length, nullptr, nullptr, nullptr));
payload_data, payload_data_length, nullptr, &video_header, nullptr));
// Min required delay until retransmit = 5 + RTT ms (RTT = 0).
fake_clock.AdvanceTimeMilliseconds(5);
int length = BuildNackList(nack_list);
Expand Down Expand Up @@ -250,9 +251,10 @@ TEST_F(RtpRtcpRtxNackTest, LongNackList) {
// Send 30 frames which at the default size is roughly what we need to get
// enough packets.
for (int frame = 0; frame < kNumFrames; ++frame) {
RTPVideoHeader video_header;
EXPECT_TRUE(rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp, timestamp / 90,
payload_data, payload_data_length, nullptr, nullptr, nullptr));
payload_data, payload_data_length, nullptr, &video_header, nullptr));
// Prepare next frame.
timestamp += 3000;
fake_clock.AdvanceTimeMilliseconds(33);
Expand Down
4 changes: 3 additions & 1 deletion modules/rtp_rtcp/source/rtp_format.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ RtpPacketizer* RtpPacketizer::Create(VideoCodecType type,
last_packet_reduction_len);
}
case kVideoCodecGeneric:
return new RtpPacketizerGeneric(frame_type, max_payload_len,
RTC_CHECK(rtp_video_header);
return new RtpPacketizerGeneric(*rtp_video_header, frame_type,
max_payload_len,
last_packet_reduction_len);
default:
RTC_NOTREACHED();
Expand Down
60 changes: 49 additions & 11 deletions modules/rtp_rtcp/source/rtp_format_video_generic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@
namespace webrtc {

static const size_t kGenericHeaderLength = 1;

RtpPacketizerGeneric::RtpPacketizerGeneric(FrameType frame_type,
size_t max_payload_len,
size_t last_packet_reduction_len)
: payload_data_(NULL),
static const size_t kExtendedHeaderLength = 2;

RtpPacketizerGeneric::RtpPacketizerGeneric(
const RTPVideoHeader& rtp_video_header,
FrameType frame_type,
size_t max_payload_len,
size_t last_packet_reduction_len)
: picture_id_(
rtp_video_header.frame_id != kNoPictureId
? absl::optional<uint16_t>(rtp_video_header.frame_id & 0x7FFF)
: absl::nullopt),
payload_data_(nullptr),
payload_size_(0),
max_payload_len_(max_payload_len - kGenericHeaderLength),
max_payload_len_(max_payload_len - kGenericHeaderLength -
(picture_id_.has_value() ? kExtendedHeaderLength : 0)),
last_packet_reduction_len_(last_packet_reduction_len),
frame_type_(frame_type),
num_packets_left_(0),
Expand Down Expand Up @@ -62,6 +70,10 @@ size_t RtpPacketizerGeneric::SetPayloadData(
if (frame_type_ == kVideoFrameKey) {
generic_header_ |= RtpFormatVideoGeneric::kKeyFrameBit;
}
if (picture_id_.has_value()) {
generic_header_ |= RtpFormatVideoGeneric::kExtendedHeaderBit;
}

return num_packets_left_;
}

Expand All @@ -86,16 +98,24 @@ bool RtpPacketizerGeneric::NextPacket(RtpPacketToSend* packet) {
}
RTC_DCHECK_LE(next_packet_payload_len, max_payload_len_);

uint8_t* out_ptr =
packet->AllocatePayload(kGenericHeaderLength + next_packet_payload_len);
size_t total_length = next_packet_payload_len + kGenericHeaderLength +
(picture_id_.has_value() ? kExtendedHeaderLength : 0);
uint8_t* out_ptr = packet->AllocatePayload(total_length);

// Put generic header in packet.
out_ptr[0] = generic_header_;
out_ptr += kGenericHeaderLength;

if (picture_id_.has_value()) {
WriteExtendedHeader(out_ptr);
out_ptr += kExtendedHeaderLength;
}

// Remove first-packet bit, following packets are intermediate.
generic_header_ &= ~RtpFormatVideoGeneric::kFirstPacketBit;

// Put payload in packet.
memcpy(out_ptr + kGenericHeaderLength, payload_data_,
next_packet_payload_len);
memcpy(out_ptr, payload_data_, next_packet_payload_len);
payload_data_ += next_packet_payload_len;
payload_size_ -= next_packet_payload_len;
--num_packets_left_;
Expand All @@ -111,14 +131,21 @@ std::string RtpPacketizerGeneric::ToString() {
return "RtpPacketizerGeneric";
}

void RtpPacketizerGeneric::WriteExtendedHeader(uint8_t* out_ptr) {
// Store bottom 15 bits of the the sequence number. Only 15 bits are used for
// compatibility with other packetizer implemenetations that also use 15 bits.
out_ptr[0] = (*picture_id_ >> 8) & 0x7F;
out_ptr[1] = *picture_id_ & 0xFF;
}

RtpDepacketizerGeneric::~RtpDepacketizerGeneric() = default;

bool RtpDepacketizerGeneric::Parse(ParsedPayload* parsed_payload,
const uint8_t* payload_data,
size_t payload_data_length) {
assert(parsed_payload != NULL);
if (payload_data_length == 0) {
RTC_LOG(LS_ERROR) << "Empty payload.";
RTC_LOG(LS_WARNING) << "Empty payload.";
return false;
}

Expand All @@ -135,6 +162,17 @@ bool RtpDepacketizerGeneric::Parse(ParsedPayload* parsed_payload,
parsed_payload->video_header().width = 0;
parsed_payload->video_header().height = 0;

if (generic_header & RtpFormatVideoGeneric::kExtendedHeaderBit) {
if (payload_data_length < kExtendedHeaderLength) {
RTC_LOG(LS_WARNING) << "Too short payload for generic header.";
return false;
}
parsed_payload->video_header().frame_id =
((payload_data[0] & 0x7F) << 8) | payload_data[1];
payload_data += kExtendedHeaderLength;
payload_data_length -= kExtendedHeaderLength;
}

parsed_payload->payload = payload_data;
parsed_payload->payload_length = payload_data_length;
return true;
Expand Down
9 changes: 8 additions & 1 deletion modules/rtp_rtcp/source/rtp_format_video_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ namespace webrtc {
namespace RtpFormatVideoGeneric {
static const uint8_t kKeyFrameBit = 0x01;
static const uint8_t kFirstPacketBit = 0x02;
// If this bit is set, there will be an extended header contained in this
// packet. This was added later so old clients will not send this.
static const uint8_t kExtendedHeaderBit = 0x04;
} // namespace RtpFormatVideoGeneric

class RtpPacketizerGeneric : public RtpPacketizer {
public:
// Initialize with payload from encoder.
// The payload_data must be exactly one encoded generic frame.
RtpPacketizerGeneric(FrameType frametype,
RtpPacketizerGeneric(const RTPVideoHeader& rtp_video_header,
FrameType frametype,
size_t max_payload_len,
size_t last_packet_reduction_len);

Expand All @@ -45,6 +49,7 @@ class RtpPacketizerGeneric : public RtpPacketizer {
std::string ToString() override;

private:
const absl::optional<uint16_t> picture_id_;
const uint8_t* payload_data_;
size_t payload_size_;
const size_t max_payload_len_;
Expand All @@ -57,6 +62,8 @@ class RtpPacketizerGeneric : public RtpPacketizer {
// Number of packets, which will be 1 byte more than the rest.
size_t num_larger_packets_;

void WriteExtendedHeader(uint8_t* out_ptr);

RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerGeneric);
};

Expand Down
112 changes: 96 additions & 16 deletions modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ TEST(RtpPacketizerVideoGeneric, AllPacketsMayBeEqualAndRespectMaxPayloadSize) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -78,8 +78,8 @@ TEST(RtpPacketizerVideoGeneric,
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -93,8 +93,8 @@ TEST(RtpPacketizerVideoGeneric,
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -114,8 +114,8 @@ TEST(RtpPacketizerVideoGeneric,
// generic header lengh for each packet minus last packet reduction).
// 4 packets is enough for kPayloadSize.
const size_t kMinNumPackets = 4;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -128,8 +128,8 @@ TEST(RtpPacketizerVideoGeneric, SomePacketsAreSmaller_RespectsMaxPayloadSize) {
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -143,8 +143,8 @@ TEST(RtpPacketizerVideoGeneric,
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -158,8 +158,8 @@ TEST(RtpPacketizerVideoGeneric,
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -179,8 +179,8 @@ TEST(RtpPacketizerVideoGeneric,
// generic header lengh for each packet minus last packet reduction).
// 5 packets is enough for kPayloadSize.
const size_t kMinNumPackets = 5;
RtpPacketizerGeneric packetizer(kVideoFrameKey, kMaxPayloadLen,
kLastPacketReductionLen);
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
Expand All @@ -189,4 +189,84 @@ TEST(RtpPacketizerVideoGeneric,
EXPECT_EQ(num_packets, kMinNumPackets);
}

TEST(RtpPacketizerVideoGeneric, HasFrameIdWritesExtendedHeader) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;

RTPVideoHeader rtp_video_header;
rtp_video_header.frame_id = 37;
RtpPacketizerGeneric packetizer(rtp_video_header, kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);

RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);

rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_TRUE(payload[0] & 0x04); // Extended header bit is set.
// Frame id is 37.
EXPECT_EQ(0u, payload[1]);
EXPECT_EQ(37u, payload[2]);
}

TEST(RtpPacketizerVideoGeneric, FrameIdOver15bitsWrapsAround) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;

RTPVideoHeader rtp_video_header;
rtp_video_header.frame_id = 0x8137;
RtpPacketizerGeneric packetizer(rtp_video_header, kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);

RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);

rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_TRUE(payload[0] & 0x04); // Extended header bit is set.
// Frame id is 0x137.
EXPECT_EQ(0x01u, payload[1]);
EXPECT_EQ(0x37u, payload[2]);
}

TEST(RtpPacketizerVideoGeneric, NoFrameIdDoesNotWriteExtendedHeader) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;

RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);

RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);

rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_FALSE(payload[0] & 0x04);
}

TEST(RtpDepacketizerVideoGeneric, NonExtendedHeaderNoFrameId) {
const size_t kPayloadLen = 1;
uint8_t payload[kPayloadLen] = {0x01};

RtpDepacketizerGeneric depacketizer;
RtpDepacketizer::ParsedPayload parsed_payload;
depacketizer.Parse(&parsed_payload, payload, kPayloadLen);

EXPECT_EQ(kNoPictureId, parsed_payload.video_header().frame_id);
}

TEST(RtpDepacketizerVideoGeneric, ExtendedHeaderParsesFrameId) {
const size_t kPayloadLen = 3;
uint8_t payload[kPayloadLen] = {0x05, 0x13, 0x37};

RtpDepacketizerGeneric depacketizer;
RtpDepacketizer::ParsedPayload parsed_payload;
depacketizer.Parse(&parsed_payload, payload, kPayloadLen);

EXPECT_EQ(0x1337, parsed_payload.video_header().frame_id);
}

} // namespace webrtc
Loading

0 comments on commit 426a80c

Please sign in to comment.