Adding a delay line to NetEq's output
This change adds an optional delay to NetEq's output. Note, this is not equivalent to increasing the jitter buffer with the same extra length. Bug: b/156734419 Change-Id: I8b70b6b3bffcfd3da296ccf29853864baa03d6bb Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175110 Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Ivo Creusen <ivoc@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31343}
This commit is contained in:
committed by
Commit Bot
parent
848ea9f0d3
commit
c49e9c253f
@ -1102,5 +1102,156 @@ TEST(NetEqNoTimeStretchingMode, RunTest) {
|
||||
EXPECT_EQ(0, stats.preemptive_rate);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Helper classes and data types and functions for NetEqOutputDelayTest.
|
||||
|
||||
class VectorAudioSink : public AudioSink {
|
||||
public:
|
||||
// Does not take ownership of the vector.
|
||||
VectorAudioSink(std::vector<int16_t>* output_vector) : v_(output_vector) {}
|
||||
|
||||
virtual ~VectorAudioSink() = default;
|
||||
|
||||
bool WriteArray(const int16_t* audio, size_t num_samples) override {
|
||||
v_->reserve(v_->size() + num_samples);
|
||||
for (size_t i = 0; i < num_samples; ++i) {
|
||||
v_->push_back(audio[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int16_t>* const v_;
|
||||
};
|
||||
|
||||
struct TestResult {
|
||||
NetEqLifetimeStatistics lifetime_stats;
|
||||
NetEqNetworkStatistics network_stats;
|
||||
absl::optional<uint32_t> playout_timestamp;
|
||||
int target_delay_ms;
|
||||
int filtered_current_delay_ms;
|
||||
int sample_rate_hz;
|
||||
};
|
||||
|
||||
// This class is used as callback object to NetEqTest to collect some stats
|
||||
// at the end of the simulation.
|
||||
class SimEndStatsCollector : public NetEqSimulationEndedCallback {
|
||||
public:
|
||||
SimEndStatsCollector(TestResult& result) : result_(result) {}
|
||||
|
||||
void SimulationEnded(int64_t /*simulation_time_ms*/, NetEq* neteq) override {
|
||||
result_.playout_timestamp = neteq->GetPlayoutTimestamp();
|
||||
result_.target_delay_ms = neteq->TargetDelayMs();
|
||||
result_.filtered_current_delay_ms = neteq->FilteredCurrentDelayMs();
|
||||
result_.sample_rate_hz = neteq->last_output_sample_rate_hz();
|
||||
}
|
||||
|
||||
private:
|
||||
TestResult& result_;
|
||||
};
|
||||
|
||||
TestResult DelayLineNetEqTest(int delay_ms,
|
||||
std::vector<int16_t>* output_vector) {
|
||||
NetEq::Config config;
|
||||
config.for_test_no_time_stretching = true;
|
||||
config.extra_output_delay_ms = delay_ms;
|
||||
auto codecs = NetEqTest::StandardDecoderMap();
|
||||
NetEqPacketSourceInput::RtpHeaderExtensionMap rtp_ext_map = {
|
||||
{1, kRtpExtensionAudioLevel},
|
||||
{3, kRtpExtensionAbsoluteSendTime},
|
||||
{5, kRtpExtensionTransportSequenceNumber},
|
||||
{7, kRtpExtensionVideoContentType},
|
||||
{8, kRtpExtensionVideoTiming}};
|
||||
std::unique_ptr<NetEqInput> input = std::make_unique<NetEqRtpDumpInput>(
|
||||
webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"),
|
||||
rtp_ext_map, absl::nullopt /*No SSRC filter*/);
|
||||
std::unique_ptr<TimeLimitedNetEqInput> input_time_limit(
|
||||
new TimeLimitedNetEqInput(std::move(input), 10000));
|
||||
std::unique_ptr<AudioSink> output =
|
||||
std::make_unique<VectorAudioSink>(output_vector);
|
||||
|
||||
TestResult result;
|
||||
SimEndStatsCollector stats_collector(result);
|
||||
NetEqTest::Callbacks callbacks;
|
||||
callbacks.simulation_ended_callback = &stats_collector;
|
||||
|
||||
NetEqTest test(config, CreateBuiltinAudioDecoderFactory(), codecs,
|
||||
/*text_log=*/nullptr, /*neteq_factory=*/nullptr,
|
||||
/*input=*/std::move(input_time_limit), std::move(output),
|
||||
callbacks);
|
||||
test.Run();
|
||||
result.lifetime_stats = test.LifetimeStats();
|
||||
result.network_stats = test.SimulationStats();
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Tests the extra output delay functionality of NetEq.
|
||||
TEST(NetEqOutputDelayTest, RunTest) {
|
||||
std::vector<int16_t> output;
|
||||
const auto result_no_delay = DelayLineNetEqTest(0, &output);
|
||||
std::vector<int16_t> output_delayed;
|
||||
constexpr int kDelayMs = 100;
|
||||
const auto result_delay = DelayLineNetEqTest(kDelayMs, &output_delayed);
|
||||
|
||||
// Verify that the loss concealment remains unchanged. The point of the delay
|
||||
// is to not affect the jitter buffering behavior.
|
||||
// First verify that there are concealments in the test.
|
||||
EXPECT_GT(result_no_delay.lifetime_stats.concealed_samples, 0u);
|
||||
// And that not all of the output is concealment.
|
||||
EXPECT_GT(result_no_delay.lifetime_stats.total_samples_received,
|
||||
result_no_delay.lifetime_stats.concealed_samples);
|
||||
// Now verify that they remain unchanged by the delay.
|
||||
EXPECT_EQ(result_no_delay.lifetime_stats.concealed_samples,
|
||||
result_delay.lifetime_stats.concealed_samples);
|
||||
// Accelerate and pre-emptive expand should also be unchanged.
|
||||
EXPECT_EQ(result_no_delay.lifetime_stats.inserted_samples_for_deceleration,
|
||||
result_delay.lifetime_stats.inserted_samples_for_deceleration);
|
||||
EXPECT_EQ(result_no_delay.lifetime_stats.removed_samples_for_acceleration,
|
||||
result_delay.lifetime_stats.removed_samples_for_acceleration);
|
||||
// Verify that delay stats are increased with the delay chain.
|
||||
EXPECT_EQ(
|
||||
result_no_delay.lifetime_stats.jitter_buffer_delay_ms +
|
||||
kDelayMs * result_no_delay.lifetime_stats.jitter_buffer_emitted_count,
|
||||
result_delay.lifetime_stats.jitter_buffer_delay_ms);
|
||||
EXPECT_EQ(
|
||||
result_no_delay.lifetime_stats.jitter_buffer_target_delay_ms +
|
||||
kDelayMs * result_no_delay.lifetime_stats.jitter_buffer_emitted_count,
|
||||
result_delay.lifetime_stats.jitter_buffer_target_delay_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.current_buffer_size_ms + kDelayMs,
|
||||
result_delay.network_stats.current_buffer_size_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.preferred_buffer_size_ms + kDelayMs,
|
||||
result_delay.network_stats.preferred_buffer_size_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.mean_waiting_time_ms + kDelayMs,
|
||||
result_delay.network_stats.mean_waiting_time_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.median_waiting_time_ms + kDelayMs,
|
||||
result_delay.network_stats.median_waiting_time_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.min_waiting_time_ms + kDelayMs,
|
||||
result_delay.network_stats.min_waiting_time_ms);
|
||||
EXPECT_EQ(result_no_delay.network_stats.max_waiting_time_ms + kDelayMs,
|
||||
result_delay.network_stats.max_waiting_time_ms);
|
||||
|
||||
ASSERT_TRUE(result_no_delay.playout_timestamp);
|
||||
ASSERT_TRUE(result_delay.playout_timestamp);
|
||||
EXPECT_EQ(*result_no_delay.playout_timestamp -
|
||||
static_cast<uint32_t>(
|
||||
kDelayMs *
|
||||
rtc::CheckedDivExact(result_no_delay.sample_rate_hz, 1000)),
|
||||
*result_delay.playout_timestamp);
|
||||
EXPECT_EQ(result_no_delay.target_delay_ms + kDelayMs,
|
||||
result_delay.target_delay_ms);
|
||||
EXPECT_EQ(result_no_delay.filtered_current_delay_ms + kDelayMs,
|
||||
result_delay.filtered_current_delay_ms);
|
||||
|
||||
// Verify expected delay in decoded signal. The test vector uses 8 kHz sample
|
||||
// rate, so the delay will be 8 times the delay in ms.
|
||||
constexpr size_t kExpectedDelaySamples = kDelayMs * 8;
|
||||
for (size_t i = 0;
|
||||
i < output.size() && i + kExpectedDelaySamples < output_delayed.size();
|
||||
++i) {
|
||||
EXPECT_EQ(output[i], output_delayed[i + kExpectedDelaySamples]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
Reference in New Issue
Block a user