Add FlexfecReceiveStream.

This class is logically parallel with the {Audio,Video}ReceiveStream
classes. Its purpose is to describe a receive stream of FlexFEC packets,
through the corresponding config.

Functionally, this class simply forwards the received RTP packets
to its FlexfecReceiver, which returns recovered packets to the
Call level, for appropriate demultiplexing based on SSRC.

BUG=webrtc:5654

Review-Url: https://codereview.webrtc.org/2397843005
Cr-Commit-Position: refs/heads/master@{#14704}
This commit is contained in:
brandtr
2016-10-20 04:54:48 -07:00
committed by Commit bot
parent 057b8d91a6
commit 76648da8dc
10 changed files with 382 additions and 0 deletions

View File

@ -24,6 +24,7 @@ rtc_source_set("call_api") {
"call/audio_send_stream.h",
"call/audio_sink.h",
"call/audio_state.h",
"call/flexfec_receive_stream.h",
]
deps = [

View File

@ -108,6 +108,7 @@
'call/audio_send_stream.h',
'call/audio_sink.h',
'call/audio_state.h',
'call/flexfec_receive_stream.h'
],
},
{

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 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 WEBRTC_API_CALL_FLEXFEC_RECEIVE_STREAM_H_
#define WEBRTC_API_CALL_FLEXFEC_RECEIVE_STREAM_H_
#include <string>
#include "webrtc/config.h"
namespace webrtc {
// WORK IN PROGRESS!
// This class is under development and it is not yet intended for use outside
// of WebRTC.
//
// TODO(brandtr): Remove this comment when FlexFEC is ready for public use.
class FlexfecReceiveStream {
public:
struct Stats {
std::string ToString(int64_t time_ms) const;
// TODO(brandtr): Add appropriate stats here.
int flexfec_bitrate_bps;
};
// TODO(brandtr): When we add multistream protection, and thus add a
// FlexfecSendStream class, remove FlexfecConfig from config.h and add
// the appropriate configs here and in FlexfecSendStream.
using Config = FlexfecConfig;
// Starts stream activity.
// When a stream is active, it can receive and process packets.
virtual void Start() = 0;
// Stops stream activity.
// When a stream is stopped, it can't receive nor process packets.
virtual void Stop() = 0;
virtual Stats GetStats() const = 0;
protected:
virtual ~FlexfecReceiveStream() = default;
};
} // namespace webrtc
#endif // WEBRTC_API_CALL_FLEXFEC_RECEIVE_STREAM_H_

View File

@ -12,6 +12,8 @@ rtc_static_library("call") {
sources = [
"bitrate_allocator.cc",
"call.cc",
"flexfec_receive_stream.cc",
"flexfec_receive_stream.h",
"transport_adapter.cc",
"transport_adapter.h",
]
@ -41,6 +43,7 @@ if (rtc_include_tests) {
"bitrate_allocator_unittest.cc",
"bitrate_estimator_tests.cc",
"call_unittest.cc",
"flexfec_receive_stream_unittest.cc",
"packet_injection_tests.cc",
]
deps = [

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2016 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 "webrtc/call/flexfec_receive_stream.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
namespace webrtc {
std::string FlexfecReceiveStream::Stats::ToString(int64_t time_ms) const {
std::stringstream ss;
ss << "FlexfecReceiveStream stats: " << time_ms
<< ", {flexfec_bitrate_bps: " << flexfec_bitrate_bps << "}";
return ss.str();
}
namespace {
// TODO(brandtr): Update this function when we support multistream protection.
std::unique_ptr<FlexfecReceiver> MaybeUpdateConfigAndCreateFlexfecReceiver(
FlexfecReceiveStream::Config* config,
RecoveredPacketReceiver* recovered_packet_callback) {
if (config->protected_media_ssrcs.empty()) {
LOG(LS_ERROR) << "No protected media SSRC supplied. "
<< "This FlexfecReceiveStream will therefore be useless.";
return nullptr;
} else if (config->protected_media_ssrcs.size() > 1) {
LOG(LS_WARNING)
<< "The supplied FlexfecConfig contained multiple protected "
"media streams, but our implementation currently only "
"supports protecting a single media stream. This "
"FlexfecReceiveStream will therefore only accept media "
"packets from the first supplied media stream, with SSRC "
<< config->protected_media_ssrcs[0] << ".";
config->protected_media_ssrcs.resize(1);
}
return FlexfecReceiver::Create(config->flexfec_ssrc,
config->protected_media_ssrcs[0],
recovered_packet_callback);
}
} // namespace
namespace internal {
FlexfecReceiveStream::FlexfecReceiveStream(
Config configuration,
RecoveredPacketReceiver* recovered_packet_callback)
: started_(false),
config_(configuration),
receiver_(MaybeUpdateConfigAndCreateFlexfecReceiver(
&config_,
recovered_packet_callback)) {
LOG(LS_INFO) << "FlexfecReceiveStream: " << config_.ToString();
}
FlexfecReceiveStream::~FlexfecReceiveStream() {
LOG(LS_INFO) << "~FlexfecReceiveStream: " << config_.ToString();
Stop();
}
bool FlexfecReceiveStream::AddAndProcessReceivedPacket(const uint8_t* packet,
size_t packet_length) {
{
rtc::CritScope cs(&crit_);
if (!started_)
return false;
}
if (!receiver_)
return false;
return receiver_->AddAndProcessReceivedPacket(packet, packet_length);
}
void FlexfecReceiveStream::Start() {
rtc::CritScope cs(&crit_);
started_ = true;
}
void FlexfecReceiveStream::Stop() {
rtc::CritScope cs(&crit_);
started_ = false;
}
// TODO(brandtr): Implement this member function when we have designed the
// stats for FlexFEC.
FlexfecReceiveStream::Stats FlexfecReceiveStream::GetStats() const {
return webrtc::FlexfecReceiveStream::Stats();
}
} // namespace internal
} // namespace webrtc

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2016 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 WEBRTC_CALL_FLEXFEC_RECEIVE_STREAM_H_
#define WEBRTC_CALL_FLEXFEC_RECEIVE_STREAM_H_
#include <memory>
#include <string>
#include "webrtc/api/call/flexfec_receive_stream.h"
#include "webrtc/base/basictypes.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/rtp_rtcp/include/flexfec_receiver.h"
namespace webrtc {
namespace internal {
class FlexfecReceiveStream : public webrtc::FlexfecReceiveStream {
public:
FlexfecReceiveStream(Config configuration,
RecoveredPacketReceiver* recovered_packet_callback);
~FlexfecReceiveStream();
const Config& config() const { return config_; }
bool AddAndProcessReceivedPacket(const uint8_t* packet, size_t length);
// Implements webrtc::FlexfecReceiveStream.
void Start() override;
void Stop() override;
Stats GetStats() const override;
private:
rtc::CriticalSection crit_;
bool started_ GUARDED_BY(crit_);
Config config_;
const std::unique_ptr<FlexfecReceiver> receiver_;
};
} // namespace internal
} // namespace webrtc
#endif // WEBRTC_CALL_FLEXFEC_RECEIVE_STREAM_H_

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2016 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 "webrtc/base/basictypes.h"
#include "webrtc/call/flexfec_receive_stream.h"
#include "webrtc/modules/rtp_rtcp/include/flexfec_receiver.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/mocks/mock_recovered_packet_receiver.h"
#include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
TEST(FlexfecReceiveStreamTest, ConstructDestruct) {
FlexfecReceiveStream::Config config;
config.flexfec_payload_type = 118;
config.flexfec_ssrc = 424223;
config.protected_media_ssrcs = {912512};
MockRecoveredPacketReceiver callback;
internal::FlexfecReceiveStream receive_stream(config, &callback);
}
TEST(FlexfecReceiveStreamTest, StartStop) {
FlexfecReceiveStream::Config config;
config.flexfec_payload_type = 118;
config.flexfec_ssrc = 1652392;
config.protected_media_ssrcs = {23300443};
MockRecoveredPacketReceiver callback;
internal::FlexfecReceiveStream receive_stream(config, &callback);
receive_stream.Start();
receive_stream.Stop();
}
TEST(FlexfecReceiveStreamTest, DoesNotProcessPacketWhenNoMediaSsrcGiven) {
FlexfecReceiveStream::Config config;
config.flexfec_payload_type = 118;
config.flexfec_ssrc = 424223;
config.protected_media_ssrcs = {};
MockRecoveredPacketReceiver callback;
internal::FlexfecReceiveStream receive_stream(config, &callback);
const uint8_t packet[] = {0x00, 0x11, 0x22, 0x33};
const size_t packet_length = sizeof(packet);
EXPECT_FALSE(
receive_stream.AddAndProcessReceivedPacket(packet, packet_length));
}
// TODO(brandtr): Remove when we support multistream protection.
TEST(FlexfecReceiveStreamTest, CannotProtectMultipleMediaStreams) {
FlexfecReceiveStream::Config config;
config.flexfec_payload_type = 118;
config.flexfec_ssrc = 424223;
config.protected_media_ssrcs = {123, 456};
MockRecoveredPacketReceiver callback;
internal::FlexfecReceiveStream receive_stream(config, &callback);
ASSERT_EQ(1U, receive_stream.config().protected_media_ssrcs.size());
EXPECT_EQ(config.protected_media_ssrcs[0],
receive_stream.config().protected_media_ssrcs[0]);
}
// Create a FlexFEC packet that protects a single media packet and ensure
// that the callback is called. Correctness of recovery is checked in the
// FlexfecReceiver unit tests.
TEST(FlexfecReceiveStreamTest, RecoversPacketWhenStarted) {
constexpr uint8_t kFlexfecPlType = 118;
constexpr uint8_t kFlexfecSeqNum[] = {0x00, 0x01};
constexpr uint8_t kFlexfecTs[] = {0x00, 0x11, 0x22, 0x33};
constexpr uint8_t kFlexfecSsrc[] = {0x00, 0x00, 0x00, 0x01};
constexpr uint8_t kMediaPlType = 107;
constexpr uint8_t kMediaSeqNum[] = {0x00, 0x02};
constexpr uint8_t kMediaTs[] = {0xaa, 0xbb, 0xcc, 0xdd};
constexpr uint8_t kMediaSsrc[] = {0x00, 0x00, 0x00, 0x02};
// This packet mask protects a single media packet, i.e., the FlexFEC payload
// is a copy of that media packet. When inserted in the FlexFEC pipeline,
// it will thus trivially recover the lost media packet.
constexpr uint8_t kKBit0 = 1 << 7;
constexpr uint8_t kFlexfecPktMask[] = {kKBit0 | 0x00, 0x01};
constexpr uint8_t kPayloadLength[] = {0x00, 0x04};
constexpr uint8_t kSsrcCount = 1;
constexpr uint8_t kReservedBits = 0x00;
constexpr uint8_t kPayloadBits = 0x00;
// clang-format off
constexpr uint8_t kFlexfecPacket[] = {
// RTP header.
0x80, kFlexfecPlType, kFlexfecSeqNum[0], kFlexfecSeqNum[1],
kFlexfecTs[0], kFlexfecTs[1], kFlexfecTs[2], kFlexfecTs[3],
kFlexfecSsrc[0], kFlexfecSsrc[1], kFlexfecSsrc[2], kFlexfecSsrc[3],
// FlexFEC header.
0x00, kMediaPlType, kPayloadLength[0], kPayloadLength[1],
kMediaTs[0], kMediaTs[1], kMediaTs[2], kMediaTs[3],
kSsrcCount, kReservedBits, kReservedBits, kReservedBits,
kMediaSsrc[0], kMediaSsrc[1], kMediaSsrc[2], kMediaSsrc[3],
kMediaSeqNum[0], kMediaSeqNum[1], kFlexfecPktMask[0], kFlexfecPktMask[1],
// FEC payload.
kPayloadBits, kPayloadBits, kPayloadBits, kPayloadBits};
// clang-format on
constexpr size_t kFlexfecPacketLength = sizeof(kFlexfecPacket);
FlexfecReceiveStream::Config config;
config.flexfec_payload_type = kFlexfecPlType;
config.flexfec_ssrc = ByteReader<uint32_t>::ReadBigEndian(kFlexfecSsrc);
config.protected_media_ssrcs = {
ByteReader<uint32_t>::ReadBigEndian(kMediaSsrc)};
testing::StrictMock<MockRecoveredPacketReceiver> recovered_packet_receiver;
internal::FlexfecReceiveStream receive_stream(config,
&recovered_packet_receiver);
// Do not call back before being started.
receive_stream.AddAndProcessReceivedPacket(kFlexfecPacket,
kFlexfecPacketLength);
// Call back after being started.
receive_stream.Start();
EXPECT_CALL(
recovered_packet_receiver,
OnRecoveredPacket(::testing::_, kRtpHeaderSize + kPayloadLength[1]));
receive_stream.AddAndProcessReceivedPacket(kFlexfecPacket,
kFlexfecPacketLength);
}
} // namespace webrtc

View File

@ -17,6 +17,8 @@
'webrtc_call_sources': [
'call/bitrate_allocator.cc',
'call/call.cc',
'call/flexfec_receive_stream.cc',
'call/flexfec_receive_stream.h',
'call/transport_adapter.cc',
'call/transport_adapter.h',
],

View File

@ -31,6 +31,20 @@ std::string UlpfecConfig::ToString() const {
return ss.str();
}
std::string FlexfecConfig::ToString() const {
std::stringstream ss;
ss << "{flexfec_payload_type: " << flexfec_payload_type;
ss << ", flexfec_ssrc: " << flexfec_ssrc;
ss << ", protected_media_ssrcs: [";
size_t i = 0;
for (; i + 1 < protected_media_ssrcs.size(); ++i)
ss << protected_media_ssrcs[i] << ", ";
if (!protected_media_ssrcs.empty())
ss << protected_media_ssrcs[i];
ss << "]}";
return ss.str();
}
std::string RtpExtension::ToString() const {
std::stringstream ss;
ss << "{uri: " << uri;

View File

@ -16,6 +16,7 @@
#include <string>
#include <vector>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/optional.h"
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ref_ptr.h"
@ -53,6 +54,26 @@ struct UlpfecConfig {
int red_rtx_payload_type;
};
// Settings for FlexFEC forward error correction.
// Set the payload type to '-1' to disable.
struct FlexfecConfig {
FlexfecConfig()
: flexfec_payload_type(-1), flexfec_ssrc(0), protected_media_ssrcs() {}
std::string ToString() const;
// Payload type of FlexFEC.
int flexfec_payload_type;
// SSRC of FlexFEC stream.
uint32_t flexfec_ssrc;
// Vector containing a single element, corresponding to the SSRC of the media
// stream being protected by this FlexFEC stream. The vector MUST have size 1.
//
// TODO(brandtr): Update comment above when we support multistream protection.
std::vector<uint32_t> protected_media_ssrcs;
};
// RTP header extension, see RFC 5285.
struct RtpExtension {
RtpExtension() : id(0) {}