Cleanup RtpPacketizerGeneric

merge SetPayloadData into constructor.
Reuse payload size split function

Bug: webrtc:9680
Change-Id: If8de22b2fc39c0bdf6d60154ecaaf19e2dccdcc9
Reviewed-on: https://webrtc-review.googlesource.com/97640
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24598}
This commit is contained in:
Danil Chapovalov
2018-09-05 16:54:22 +02:00
committed by Commit Bot
parent 8a7916b3f8
commit af8c03673b
4 changed files with 136 additions and 286 deletions

View File

@ -53,11 +53,8 @@ std::unique_ptr<RtpPacketizer> RtpPacketizer::Create(
return std::move(packetizer);
}
default: {
auto packetizer = absl::make_unique<RtpPacketizerGeneric>(
rtp_video_header, frame_type, limits.max_payload_len,
limits.last_packet_reduction_len);
packetizer->SetPayloadData(payload.data(), payload.size(), nullptr);
return std::move(packetizer);
return absl::make_unique<RtpPacketizerGeneric>(
payload, limits, rtp_video_header, frame_type);
}
}
}

View File

@ -21,121 +21,70 @@ static const size_t kGenericHeaderLength = 1;
static const size_t kExtendedHeaderLength = 2;
RtpPacketizerGeneric::RtpPacketizerGeneric(
rtc::ArrayView<const uint8_t> payload,
PayloadSizeLimits limits,
const RTPVideoHeader& rtp_video_header,
FrameType frame_type,
size_t max_payload_len,
size_t last_packet_reduction_len)
: picture_id_(rtp_video_header.generic
? absl::optional<uint16_t>(
rtp_video_header.generic->frame_id & 0x7FFF)
: absl::nullopt),
payload_data_(nullptr),
payload_size_(0),
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),
num_larger_packets_(0) {}
FrameType frame_type)
: remaining_payload_(payload) {
BuildHeader(rtp_video_header, frame_type);
RtpPacketizerGeneric::~RtpPacketizerGeneric() {}
size_t RtpPacketizerGeneric::SetPayloadData(
const uint8_t* payload_data,
size_t payload_size,
const RTPFragmentationHeader* fragmentation) {
payload_data_ = payload_data;
payload_size_ = payload_size;
// Fragment packets such that they are almost the same size, even accounting
// for larger header in the last packet.
// Since we are given how much extra space is occupied by the longer header
// in the last packet, we can pretend that RTP headers are the same, but
// there's last_packet_reduction_len_ virtual payload, to be put at the end of
// the last packet.
//
size_t total_bytes = payload_size_ + last_packet_reduction_len_;
// Minimum needed number of packets to fit payload and virtual payload in the
// last packet.
num_packets_left_ = (total_bytes + max_payload_len_ - 1) / max_payload_len_;
// Given number of packets, calculate average size rounded down.
payload_len_per_packet_ = total_bytes / num_packets_left_;
// If we can't divide everything perfectly evenly, we put 1 extra byte in some
// last packets: 14 bytes in 4 packets would be split as 3+3+4+4.
num_larger_packets_ = total_bytes % num_packets_left_;
RTC_DCHECK_LE(payload_len_per_packet_, max_payload_len_);
generic_header_ = RtpFormatVideoGeneric::kFirstPacketBit;
if (frame_type_ == kVideoFrameKey) {
generic_header_ |= RtpFormatVideoGeneric::kKeyFrameBit;
}
if (picture_id_.has_value()) {
generic_header_ |= RtpFormatVideoGeneric::kExtendedHeaderBit;
}
return num_packets_left_;
limits.max_payload_len -= header_size_;
payload_sizes_ = SplitAboutEqually(payload.size(), limits);
current_packet_ = payload_sizes_.begin();
}
RtpPacketizerGeneric::~RtpPacketizerGeneric() = default;
size_t RtpPacketizerGeneric::NumPackets() const {
return num_packets_left_;
return payload_sizes_.end() - current_packet_;
}
bool RtpPacketizerGeneric::NextPacket(RtpPacketToSend* packet) {
RTC_DCHECK(packet);
if (num_packets_left_ == 0)
if (current_packet_ == payload_sizes_.end())
return false;
// Last larger_packets_ packets are 1 byte larger than previous packets.
// Increase per packet payload once needed.
if (num_packets_left_ == num_larger_packets_)
++payload_len_per_packet_;
size_t next_packet_payload_len = payload_len_per_packet_;
if (payload_size_ <= next_packet_payload_len) {
// Whole payload fits into this packet.
next_packet_payload_len = payload_size_;
if (num_packets_left_ == 2) {
// This is the penultimate packet. Leave at least 1 payload byte for the
// last packet.
--next_packet_payload_len;
RTC_DCHECK_GT(next_packet_payload_len, 0);
}
}
RTC_DCHECK_LE(next_packet_payload_len, max_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);
size_t next_packet_payload_len = *current_packet_;
// Put generic header in packet.
out_ptr[0] = generic_header_;
out_ptr += kGenericHeaderLength;
uint8_t* out_ptr =
packet->AllocatePayload(header_size_ + next_packet_payload_len);
RTC_CHECK(out_ptr);
if (picture_id_.has_value()) {
WriteExtendedHeader(out_ptr);
out_ptr += kExtendedHeaderLength;
}
memcpy(out_ptr, header_, header_size_);
memcpy(out_ptr + header_size_, remaining_payload_.data(),
next_packet_payload_len);
// Remove first-packet bit, following packets are intermediate.
generic_header_ &= ~RtpFormatVideoGeneric::kFirstPacketBit;
header_[0] &= ~RtpFormatVideoGeneric::kFirstPacketBit;
remaining_payload_ = remaining_payload_.subview(next_packet_payload_len);
++current_packet_;
// Put payload in packet.
memcpy(out_ptr, payload_data_, next_packet_payload_len);
payload_data_ += next_packet_payload_len;
payload_size_ -= next_packet_payload_len;
--num_packets_left_;
// Packets left to produce and data left to split should end at the same time.
RTC_DCHECK_EQ(num_packets_left_ == 0, payload_size_ == 0);
packet->SetMarker(payload_size_ == 0);
RTC_DCHECK_EQ(current_packet_ == payload_sizes_.end(),
remaining_payload_.empty());
packet->SetMarker(remaining_payload_.empty());
return true;
}
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;
void RtpPacketizerGeneric::BuildHeader(const RTPVideoHeader& rtp_video_header,
FrameType frame_type) {
header_size_ = kGenericHeaderLength;
header_[0] = RtpFormatVideoGeneric::kFirstPacketBit;
if (frame_type == kVideoFrameKey) {
header_[0] |= RtpFormatVideoGeneric::kKeyFrameBit;
}
if (rtp_video_header.generic.has_value()) {
// Store bottom 15 bits of the the picture id. Only 15 bits are used for
// compatibility with other packetizer implemenetations.
uint16_t picture_id = rtp_video_header.generic->frame_id & 0x7FFF;
header_[0] |= RtpFormatVideoGeneric::kExtendedHeaderBit;
header_[1] = (picture_id >> 8) & 0x7F;
header_[2] = picture_id & 0xFF;
header_size_ += kExtendedHeaderLength;
}
}
RtpDepacketizerGeneric::~RtpDepacketizerGeneric() = default;

View File

@ -10,8 +10,9 @@
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_
#define MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_
#include <string>
#include <vector>
#include "api/array_view.h"
#include "common_types.h" // NOLINT(build/include)
#include "modules/rtp_rtcp/source/rtp_format.h"
#include "rtc_base/constructormagic.h"
@ -29,18 +30,13 @@ class RtpPacketizerGeneric : public RtpPacketizer {
public:
// Initialize with payload from encoder.
// The payload_data must be exactly one encoded generic frame.
RtpPacketizerGeneric(const RTPVideoHeader& rtp_video_header,
FrameType frametype,
size_t max_payload_len,
size_t last_packet_reduction_len);
RtpPacketizerGeneric(rtc::ArrayView<const uint8_t> payload,
PayloadSizeLimits limits,
const RTPVideoHeader& rtp_video_header,
FrameType frametype);
~RtpPacketizerGeneric() override;
// Returns total number of packets to be generated.
size_t SetPayloadData(const uint8_t* payload_data,
size_t payload_size,
const RTPFragmentationHeader* fragmentation);
size_t NumPackets() const override;
// Get the next payload with generic payload header.
@ -49,20 +45,15 @@ class RtpPacketizerGeneric : public RtpPacketizer {
bool NextPacket(RtpPacketToSend* packet) override;
private:
const absl::optional<uint16_t> picture_id_;
const uint8_t* payload_data_;
size_t payload_size_;
const size_t max_payload_len_;
const size_t last_packet_reduction_len_;
FrameType frame_type_;
size_t payload_len_per_packet_;
uint8_t generic_header_;
// Number of packets yet to be retrieved by NextPacket() call.
size_t num_packets_left_;
// Number of packets, which will be 1 byte more than the rest.
size_t num_larger_packets_;
// Fills header_ and header_size_ members.
void BuildHeader(const RTPVideoHeader& rtp_video_header,
FrameType frame_type);
void WriteExtendedHeader(uint8_t* out_ptr);
uint8_t header_[3];
size_t header_size_;
rtc::ArrayView<const uint8_t> remaining_payload_;
std::vector<size_t> payload_sizes_;
std::vector<size_t>::const_iterator current_packet_;
RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerGeneric);
};

View File

@ -29,10 +29,9 @@ using ::testing::Each;
using ::testing::ElementsAreArray;
using ::testing::Le;
using ::testing::SizeIs;
using ::testing::Contains;
const size_t kMaxPayloadSize = 1200;
uint8_t kTestPayload[kMaxPayloadSize];
constexpr RtpPacketizer::PayloadSizeLimits kNoSizeLimits;
std::vector<size_t> NextPacketFillPayloadSizes(
RtpPacketizerGeneric* packetizer) {
@ -44,185 +43,100 @@ std::vector<size_t> NextPacketFillPayloadSizes(
return result;
}
size_t GetEffectivePacketsSizeDifference(std::vector<size_t>* payload_sizes,
size_t last_packet_reduction_len) {
// Account for larger last packet header.
payload_sizes->back() += last_packet_reduction_len;
auto minmax =
std::minmax_element(payload_sizes->begin(), payload_sizes->end());
// MAX-MIN
size_t difference = *minmax.second - *minmax.first;
// Revert temporary changes.
payload_sizes->back() -= last_packet_reduction_len;
return difference;
TEST(RtpPacketizerVideoGeneric, RespectsMaxPayloadSize) {
const size_t kPayloadSize = 50;
const uint8_t kPayload[kPayloadSize] = {};
RtpPacketizer::PayloadSizeLimits limits;
limits.max_payload_len = 6;
RtpPacketizerGeneric packetizer(kPayload, limits, RTPVideoHeader(),
kVideoFrameKey);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
}
} // namespace
TEST(RtpPacketizerVideoGeneric, UsesMaxPayloadSize) {
const size_t kPayloadSize = 50;
const uint8_t kPayload[kPayloadSize] = {};
TEST(RtpPacketizerVideoGeneric, AllPacketsMayBeEqualAndRespectMaxPayloadSize) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_THAT(payload_sizes, Each(Le(kMaxPayloadLen)));
}
TEST(RtpPacketizerVideoGeneric,
AllPacketsMayBeEqual_RespectsLastPacketReductionLength) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_LE(payload_sizes.back(), kMaxPayloadLen - kLastPacketReductionLen);
}
TEST(RtpPacketizerVideoGeneric,
AllPacketsMayBeEqual_MakesPacketsAlmostEqualInSize) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
size_t sizes_difference = GetEffectivePacketsSizeDifference(
&payload_sizes, kLastPacketReductionLen);
EXPECT_LE(sizes_difference, 1u);
}
TEST(RtpPacketizerVideoGeneric,
AllPacketsMayBeEqual_GeneratesMinimumNumberOfPackets) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 3;
const size_t kPayloadSize = 13;
// Computed by hand. 3 packets would have capacity 3*(6-1)-3=12 (max length -
// generic header lengh for each packet minus last packet reduction).
// 4 packets is enough for kPayloadSize.
const size_t kMinNumPackets = 4;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_EQ(num_packets, kMinNumPackets);
}
TEST(RtpPacketizerVideoGeneric, SomePacketsAreSmaller_RespectsMaxPayloadSize) {
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_THAT(payload_sizes, Each(Le(kMaxPayloadLen)));
}
TEST(RtpPacketizerVideoGeneric,
SomePacketsAreSmaller_RespectsLastPacketReductionLength) {
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_LE(payload_sizes.back(), kMaxPayloadLen - kLastPacketReductionLen);
}
TEST(RtpPacketizerVideoGeneric,
SomePacketsAreSmaller_MakesPacketsAlmostEqualInSize) {
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
size_t sizes_difference = GetEffectivePacketsSizeDifference(
&payload_sizes, kLastPacketReductionLen);
EXPECT_LE(sizes_difference, 1u);
}
TEST(RtpPacketizerVideoGeneric,
SomePacketsAreSmaller_GeneratesMinimumNumberOfPackets) {
const size_t kMaxPayloadLen = 8;
const size_t kLastPacketReductionLen = 5;
const size_t kPayloadSize = 28;
// Computed by hand. 4 packets would have capacity 4*(8-1)-5=23 (max length -
// generic header lengh for each packet minus last packet reduction).
// 5 packets is enough for kPayloadSize.
const size_t kMinNumPackets = 5;
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
size_t num_packets =
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, SizeIs(num_packets));
EXPECT_EQ(num_packets, kMinNumPackets);
}
TEST(RtpPacketizerVideoGeneric, HasFrameIdWritesExtendedHeader) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
RtpPacketizer::PayloadSizeLimits limits;
limits.max_payload_len = 6;
RtpPacketizerGeneric packetizer(kPayload, limits, RTPVideoHeader(),
kVideoFrameKey);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
// With kPayloadSize > max_payload_len^2, there should be packets that use
// all the payload, otherwise it is possible to use less packets.
EXPECT_THAT(payload_sizes, Contains(limits.max_payload_len));
}
TEST(RtpPacketizerVideoGeneric, WritesExtendedHeaderWhenPictureIdIsSet) {
const size_t kPayloadSize = 13;
const uint8_t kPayload[kPayloadSize] = {};
RTPVideoHeader rtp_video_header;
rtp_video_header.generic.emplace().frame_id = 37;
RtpPacketizerGeneric packetizer(rtp_video_header, kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
RtpPacketizerGeneric packetizer(kPayload, kNoSizeLimits, rtp_video_header,
kVideoFrameKey);
RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);
ASSERT_TRUE(packetizer.NextPacket(&packet));
rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_EQ(payload.size(), 3 + kPayloadSize);
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, RespectsMaxPayloadSizeWithExtendedHeader) {
const size_t kPayloadSize = 50;
const uint8_t kPayload[kPayloadSize] = {};
RtpPacketizer::PayloadSizeLimits limits;
limits.max_payload_len = 6;
RTPVideoHeader rtp_video_header;
rtp_video_header.generic.emplace().frame_id = 37;
RtpPacketizerGeneric packetizer(kPayload, limits, rtp_video_header,
kVideoFrameKey);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
EXPECT_THAT(payload_sizes, Each(Le(limits.max_payload_len)));
}
TEST(RtpPacketizerVideoGeneric, UsesMaxPayloadSizeWithExtendedHeader) {
const size_t kPayloadSize = 50;
const uint8_t kPayload[kPayloadSize] = {};
RtpPacketizer::PayloadSizeLimits limits;
limits.max_payload_len = 6;
RTPVideoHeader rtp_video_header;
rtp_video_header.generic.emplace().frame_id = 37;
RtpPacketizerGeneric packetizer(kPayload, limits, rtp_video_header,
kVideoFrameKey);
std::vector<size_t> payload_sizes = NextPacketFillPayloadSizes(&packetizer);
// With kPayloadSize > max_payload_len^2, there should be packets that use
// all the payload, otherwise it is possible to use less packets.
EXPECT_THAT(payload_sizes, Contains(limits.max_payload_len));
}
TEST(RtpPacketizerVideoGeneric, FrameIdOver15bitsWrapsAround) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
const uint8_t kPayload[kPayloadSize] = {};
RTPVideoHeader rtp_video_header;
rtp_video_header.generic.emplace().frame_id = 0x8137;
RtpPacketizerGeneric packetizer(rtp_video_header, kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
RtpPacketizerGeneric packetizer(kPayload, kNoSizeLimits, rtp_video_header,
kVideoFrameKey);
RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);
ASSERT_TRUE(packetizer.NextPacket(&packet));
rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_TRUE(payload[0] & 0x04); // Extended header bit is set.
@ -232,16 +146,14 @@ TEST(RtpPacketizerVideoGeneric, FrameIdOver15bitsWrapsAround) {
}
TEST(RtpPacketizerVideoGeneric, NoFrameIdDoesNotWriteExtendedHeader) {
const size_t kMaxPayloadLen = 6;
const size_t kLastPacketReductionLen = 2;
const size_t kPayloadSize = 13;
const uint8_t kPayload[kPayloadSize] = {};
RtpPacketizerGeneric packetizer(RTPVideoHeader(), kVideoFrameKey,
kMaxPayloadLen, kLastPacketReductionLen);
packetizer.SetPayloadData(kTestPayload, kPayloadSize, nullptr);
RtpPacketizerGeneric packetizer(kPayload, kNoSizeLimits, RTPVideoHeader(),
kVideoFrameKey);
RtpPacketToSend packet(nullptr);
packetizer.NextPacket(&packet);
ASSERT_TRUE(packetizer.NextPacket(&packet));
rtc::ArrayView<const uint8_t> payload = packet.payload();
EXPECT_FALSE(payload[0] & 0x04);
@ -270,4 +182,5 @@ TEST(RtpDepacketizerVideoGeneric, ExtendedHeaderParsesFrameId) {
EXPECT_EQ(0x1337, parsed_payload.video_header().generic->frame_id);
}
} // namespace
} // namespace webrtc