Add Parsing/Building generic frame descriptor extension

Bug: webrtc:9361
Change-Id: I7e85826117348e2d4f4726e8d515bb1d4a289966
Reviewed-on: https://webrtc-review.googlesource.com/83622
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23662}
This commit is contained in:
Danil Chapovalov
2018-06-19 14:24:17 +02:00
committed by Commit Bot
parent 709c82200f
commit faf282700c
6 changed files with 465 additions and 2 deletions

View File

@ -41,6 +41,7 @@ rtc_source_set("rtp_rtcp_format") {
"source/rtcp_packet/transport_feedback.h",
"source/rtcp_packet/voip_metric.h",
"source/rtp_generic_frame_descriptor.h",
"source/rtp_generic_frame_descriptor_extension.h",
"source/rtp_header_extensions.h",
"source/rtp_packet.h",
"source/rtp_packet_received.h",
@ -75,6 +76,7 @@ rtc_source_set("rtp_rtcp_format") {
"source/rtcp_packet/transport_feedback.cc",
"source/rtcp_packet/voip_metric.cc",
"source/rtp_generic_frame_descriptor.cc",
"source/rtp_generic_frame_descriptor_extension.cc",
"source/rtp_header_extension_map.cc",
"source/rtp_header_extensions.cc",
"source/rtp_packet.cc",
@ -381,6 +383,7 @@ if (rtc_include_tests) {
"source/rtp_format_vp8_test_helper.h",
"source/rtp_format_vp8_unittest.cc",
"source/rtp_format_vp9_unittest.cc",
"source/rtp_generic_frame_descriptor_extension_unittest.cc",
"source/rtp_header_extension_map_unittest.cc",
"source/rtp_packet_history_unittest.cc",
"source/rtp_packet_unittest.cc",

View File

@ -51,7 +51,7 @@ void RtpGenericFrameDescriptor::SetFrameId(uint16_t frame_id) {
}
rtc::ArrayView<const uint16_t>
RtpGenericFrameDescriptor::FrameDepedenciesDiffs() const {
RtpGenericFrameDescriptor::FrameDependenciesDiffs() const {
RTC_DCHECK(FirstPacketInSubFrame());
return rtc::MakeArrayView(frame_deps_id_diffs_, num_frame_deps_);
}

View File

@ -45,7 +45,7 @@ class RtpGenericFrameDescriptor {
uint16_t FrameId() const;
void SetFrameId(uint16_t frame_id);
rtc::ArrayView<const uint16_t> FrameDepedenciesDiffs() const;
rtc::ArrayView<const uint16_t> FrameDependenciesDiffs() const;
void ClearFrameDependencies() { num_frame_deps_ = 0; }
// Returns false on failure, i.e. number of dependencies is too large.
bool AddFrameDependencyDiff(uint16_t fdiff);

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
constexpr uint8_t kFlagBeginOfSubframe = 0x80;
constexpr uint8_t kFlagEndOfSubframe = 0x40;
constexpr uint8_t kFlagFirstSubframe = 0x20;
constexpr uint8_t kFlagLastSubframe = 0x10;
constexpr uint8_t kFlagDependencies = 0x08;
constexpr uint8_t kMaskTemporalLayer = 0x07;
constexpr uint8_t kFlagMoreDependencies = 0x01;
constexpr uint8_t kFlageXtendedOffset = 0x02;
} // namespace
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |B|E|F|L|D| T |
// +-+-+-+-+-+-+-+-+
// B: | S |
// +-+-+-+-+-+-+-+-+
// | |
// B: + FID +
// | |
// +-+-+-+-+-+-+-+-+
// D: | FDIFF |X|M|
// +---------------+
// X: | ... |
// +-+-+-+-+-+-+-+-+
// M: | FDIFF |X|M|
// +---------------+
// | ... |
// +-+-+-+-+-+-+-+-+
bool RtpGenericFrameDescriptorExtension::Parse(
rtc::ArrayView<const uint8_t> data,
RtpGenericFrameDescriptor* descriptor) {
if (data.empty()) {
return false;
}
bool begins_subframe = (data[0] & kFlagBeginOfSubframe) != 0;
descriptor->SetFirstPacketInSubFrame(begins_subframe);
descriptor->SetLastPacketInSubFrame((data[0] & kFlagEndOfSubframe) != 0);
descriptor->SetFirstSubFrameInFrame((data[0] & kFlagFirstSubframe) != 0);
descriptor->SetLastSubFrameInFrame((data[0] & kFlagLastSubframe) != 0);
// Parse Subframe details provided in 1st packet of subframe.
if (!begins_subframe) {
return data.size() == 1;
}
if (data.size() < 4) {
return false;
}
descriptor->SetTemporalLayer(data[0] & kMaskTemporalLayer);
descriptor->SetSpatialLayersBitmask(data[1]);
descriptor->SetFrameId(data[2] | (data[3] << 8));
// Parse dependencies.
descriptor->ClearFrameDependencies();
size_t offset = 4;
bool has_more_dependencies = (data[0] & kFlagDependencies) != 0;
while (has_more_dependencies) {
if (data.size() == offset)
return false;
has_more_dependencies = (data[offset] & kFlagMoreDependencies) != 0;
bool extended = (data[offset] & kFlageXtendedOffset) != 0;
uint16_t fdiff = data[offset] >> 2;
offset++;
if (extended) {
if (data.size() == offset)
return false;
fdiff |= (data[offset] << 6);
offset++;
}
if (!descriptor->AddFrameDependencyDiff(fdiff))
return false;
}
return data.size() == offset;
}
size_t RtpGenericFrameDescriptorExtension::ValueSize(
const RtpGenericFrameDescriptor& descriptor) {
if (!descriptor.FirstPacketInSubFrame())
return 1;
size_t size = 4;
for (uint16_t fdiff : descriptor.FrameDependenciesDiffs()) {
size += (fdiff >= (1 << 6)) ? 2 : 1;
}
return size;
}
bool RtpGenericFrameDescriptorExtension::Write(
rtc::ArrayView<uint8_t> data,
const RtpGenericFrameDescriptor& descriptor) {
RTC_CHECK_EQ(data.size(), ValueSize(descriptor));
uint8_t base_header =
(descriptor.FirstPacketInSubFrame() ? kFlagBeginOfSubframe : 0) |
(descriptor.LastPacketInSubFrame() ? kFlagEndOfSubframe : 0) |
(descriptor.FirstSubFrameInFrame() ? kFlagFirstSubframe : 0) |
(descriptor.LastSubFrameInFrame() ? kFlagLastSubframe : 0);
if (!descriptor.FirstPacketInSubFrame()) {
data[0] = base_header;
return true;
}
data[0] =
base_header |
(descriptor.FrameDependenciesDiffs().empty() ? 0 : kFlagDependencies) |
descriptor.TemporalLayer();
data[1] = descriptor.SpatialLayersBitmask();
uint16_t frame_id = descriptor.FrameId();
data[2] = frame_id & 0xff;
data[3] = frame_id >> 8;
rtc::ArrayView<const uint16_t> fdiffs = descriptor.FrameDependenciesDiffs();
size_t offset = 4;
for (size_t i = 0; i < fdiffs.size(); i++) {
bool extended = fdiffs[i] >= (1 << 6);
bool more = i < fdiffs.size() - 1;
data[offset++] = ((fdiffs[i] & 0x3f) << 2) |
(extended ? kFlageXtendedOffset : 0) |
(more ? kFlagMoreDependencies : 0);
if (extended) {
data[offset++] = fdiffs[i] >> 6;
}
}
return true;
}
} // namespace webrtc

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_
#define MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_
#include <stddef.h>
#include <stdint.h>
#include "api/array_view.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
namespace webrtc {
class RtpGenericFrameDescriptorExtension {
public:
// TODO(bugs.webrtc.org/9361): Add kId and kUri to make it extension trait.
static bool Parse(rtc::ArrayView<const uint8_t> data,
RtpGenericFrameDescriptor* descriptor);
static size_t ValueSize(const RtpGenericFrameDescriptor&);
static bool Write(rtc::ArrayView<uint8_t> data,
const RtpGenericFrameDescriptor& descriptor);
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_

View File

@ -0,0 +1,283 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
// TODO(danilchap): Add fuzzer to test for various invalid inputs.
TEST(RtpGenericFrameDescriptorExtensionTest,
ParseFirstPacketOfIndependenSubFrame) {
const int kTemporalLayer = 5;
constexpr uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
EXPECT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_FALSE(descriptor.LastPacketInSubFrame());
EXPECT_FALSE(descriptor.FirstSubFrameInFrame());
EXPECT_FALSE(descriptor.LastSubFrameInFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), IsEmpty());
EXPECT_EQ(descriptor.TemporalLayer(), kTemporalLayer);
EXPECT_EQ(descriptor.SpatialLayersBitmask(), 0x49);
EXPECT_EQ(descriptor.FrameId(), 0x3412);
}
TEST(RtpGenericFrameDescriptorExtensionTest,
WriteFirstPacketOfIndependenSubFrame) {
const int kTemporalLayer = 5;
constexpr uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.SetTemporalLayer(kTemporalLayer);
descriptor.SetSpatialLayersBitmask(0x49);
descriptor.SetFrameId(0x3412);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseLastPacketOfSubFrame) {
constexpr uint8_t kRaw[] = {0x40};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
EXPECT_FALSE(descriptor.FirstPacketInSubFrame());
EXPECT_TRUE(descriptor.LastPacketInSubFrame());
EXPECT_FALSE(descriptor.FirstSubFrameInFrame());
EXPECT_FALSE(descriptor.LastSubFrameInFrame());
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteLastPacketOfSubFrame) {
constexpr uint8_t kRaw[] = {0x40};
RtpGenericFrameDescriptor descriptor;
descriptor.SetLastPacketInSubFrame(true);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseFirstSubFrameInFrame) {
constexpr uint8_t kRaw[] = {0x20};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
EXPECT_FALSE(descriptor.FirstPacketInSubFrame());
EXPECT_FALSE(descriptor.LastPacketInSubFrame());
EXPECT_TRUE(descriptor.FirstSubFrameInFrame());
EXPECT_FALSE(descriptor.LastSubFrameInFrame());
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteFirstSubFrameInFrame) {
constexpr uint8_t kRaw[] = {0x20};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstSubFrameInFrame(true);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseLastSubFrameInFrame) {
constexpr uint8_t kRaw[] = {0x10};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
EXPECT_FALSE(descriptor.FirstPacketInSubFrame());
EXPECT_FALSE(descriptor.LastPacketInSubFrame());
EXPECT_FALSE(descriptor.FirstSubFrameInFrame());
EXPECT_TRUE(descriptor.LastSubFrameInFrame());
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteLastSubFrameInFrame) {
constexpr uint8_t kRaw[] = {0x10};
RtpGenericFrameDescriptor descriptor;
descriptor.SetLastSubFrameInFrame(true);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinShortFrameDependencies) {
constexpr uint16_t kDiff = 1;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x04};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinShortFrameDependencies) {
constexpr uint16_t kDiff = 1;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x04};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxShortFrameDependencies) {
constexpr uint16_t kDiff = 0x3f;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfc};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxShortFrameDependencies) {
constexpr uint16_t kDiff = 0x3f;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfc};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinLongFrameDependencies) {
constexpr uint16_t kDiff = 0x40;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x02, 0x01};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinLongFrameDependencies) {
constexpr uint16_t kDiff = 0x40;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x02, 0x01};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest,
ParseLongFrameDependenciesAsBigEndian) {
constexpr uint16_t kDiff = 0x7654 >> 2;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
}
TEST(RtpGenericFrameDescriptorExtensionTest,
WriteLongFrameDependenciesAsBigEndian) {
constexpr uint16_t kDiff = 0x7654 >> 2;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxLongFrameDependencies) {
constexpr uint16_t kDiff = 0x3fff;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfe, 0xff};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxLongFrameDependencies) {
constexpr uint16_t kDiff = 0x3fff;
constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfe, 0xff};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
TEST(RtpGenericFrameDescriptorExtensionTest, ParseTwoFrameDependencies) {
constexpr uint16_t kDiff1 = 9;
constexpr uint16_t kDiff2 = 15;
constexpr uint8_t kRaw[] = {
0x88, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2};
RtpGenericFrameDescriptor descriptor;
ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff1, kDiff2));
}
TEST(RtpGenericFrameDescriptorExtensionTest, WriteTwoFrameDependencies) {
constexpr uint16_t kDiff1 = 9;
constexpr uint16_t kDiff2 = 15;
constexpr uint8_t kRaw[] = {
0x88, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2};
RtpGenericFrameDescriptor descriptor;
descriptor.SetFirstPacketInSubFrame(true);
descriptor.AddFrameDependencyDiff(kDiff1);
descriptor.AddFrameDependencyDiff(kDiff2);
ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
sizeof(kRaw));
uint8_t buffer[sizeof(kRaw)];
EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
EXPECT_THAT(buffer, ElementsAreArray(kRaw));
}
} // namespace
} // namespace webrtc