1 /*
2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 // Test to verify correct operation when using the decoder-internal PLC.
12
13 #include <algorithm>
14 #include <memory>
15 #include <utility>
16 #include <vector>
17
18 #include "absl/types/optional.h"
19 #include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
20 #include "modules/audio_coding/neteq/tools/audio_checksum.h"
21 #include "modules/audio_coding/neteq/tools/audio_sink.h"
22 #include "modules/audio_coding/neteq/tools/encode_neteq_input.h"
23 #include "modules/audio_coding/neteq/tools/fake_decode_from_file.h"
24 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
25 #include "modules/audio_coding/neteq/tools/neteq_test.h"
26 #include "rtc_base/numerics/safe_conversions.h"
27 #include "rtc_base/ref_counted_object.h"
28 #include "test/audio_decoder_proxy_factory.h"
29 #include "test/gtest.h"
30 #include "test/testsupport/file_utils.h"
31
32 namespace webrtc {
33 namespace test {
34 namespace {
35
36 // This class implements a fake decoder. The decoder will read audio from a file
37 // and present as output, both for regular decoding and for PLC.
38 class AudioDecoderPlc : public AudioDecoder {
39 public:
AudioDecoderPlc(std::unique_ptr<InputAudioFile> input,int sample_rate_hz)40 AudioDecoderPlc(std::unique_ptr<InputAudioFile> input, int sample_rate_hz)
41 : input_(std::move(input)), sample_rate_hz_(sample_rate_hz) {}
42
Reset()43 void Reset() override {}
SampleRateHz() const44 int SampleRateHz() const override { return sample_rate_hz_; }
Channels() const45 size_t Channels() const override { return 1; }
DecodeInternal(const uint8_t *,size_t encoded_len,int sample_rate_hz,int16_t * decoded,SpeechType * speech_type)46 int DecodeInternal(const uint8_t* /*encoded*/,
47 size_t encoded_len,
48 int sample_rate_hz,
49 int16_t* decoded,
50 SpeechType* speech_type) override {
51 RTC_CHECK_EQ(encoded_len / 2, 20 * sample_rate_hz_ / 1000);
52 RTC_CHECK_EQ(sample_rate_hz, sample_rate_hz_);
53 RTC_CHECK(decoded);
54 RTC_CHECK(speech_type);
55 RTC_CHECK(input_->Read(encoded_len / 2, decoded));
56 *speech_type = kSpeech;
57 last_was_plc_ = false;
58 return encoded_len / 2;
59 }
60
GeneratePlc(size_t requested_samples_per_channel,rtc::BufferT<int16_t> * concealment_audio)61 void GeneratePlc(size_t requested_samples_per_channel,
62 rtc::BufferT<int16_t>* concealment_audio) override {
63 // Must keep a local copy of this since DecodeInternal sets it to false.
64 const bool last_was_plc = last_was_plc_;
65 SpeechType speech_type;
66 std::vector<int16_t> decoded(5760);
67 int dec_len = DecodeInternal(nullptr, 2 * 20 * sample_rate_hz_ / 1000,
68 sample_rate_hz_, decoded.data(), &speech_type);
69 // This fake decoder can only generate 20 ms of PLC data each time. Make
70 // sure the caller didn't ask for more.
71 RTC_CHECK_GE(dec_len, requested_samples_per_channel);
72 concealment_audio->AppendData(decoded.data(), dec_len);
73 concealed_samples_ += rtc::checked_cast<size_t>(dec_len);
74 if (!last_was_plc) {
75 ++concealment_events_;
76 }
77 last_was_plc_ = true;
78 }
79
concealed_samples()80 size_t concealed_samples() { return concealed_samples_; }
concealment_events()81 size_t concealment_events() { return concealment_events_; }
82
83 private:
84 const std::unique_ptr<InputAudioFile> input_;
85 const int sample_rate_hz_;
86 size_t concealed_samples_ = 0;
87 size_t concealment_events_ = 0;
88 bool last_was_plc_ = false;
89 };
90
91 // An input sample generator which generates only zero-samples.
92 class ZeroSampleGenerator : public EncodeNetEqInput::Generator {
93 public:
Generate(size_t num_samples)94 rtc::ArrayView<const int16_t> Generate(size_t num_samples) override {
95 vec.resize(num_samples, 0);
96 rtc::ArrayView<const int16_t> view(vec);
97 RTC_DCHECK_EQ(view.size(), num_samples);
98 return view;
99 }
100
101 private:
102 std::vector<int16_t> vec;
103 };
104
105 // A NetEqInput which connects to another NetEqInput, but drops a number of
106 // packets on the way.
107 class LossyInput : public NetEqInput {
108 public:
LossyInput(int loss_cadence,std::unique_ptr<NetEqInput> input)109 LossyInput(int loss_cadence, std::unique_ptr<NetEqInput> input)
110 : loss_cadence_(loss_cadence), input_(std::move(input)) {}
111
NextPacketTime() const112 absl::optional<int64_t> NextPacketTime() const override {
113 return input_->NextPacketTime();
114 }
115
NextOutputEventTime() const116 absl::optional<int64_t> NextOutputEventTime() const override {
117 return input_->NextOutputEventTime();
118 }
119
PopPacket()120 std::unique_ptr<PacketData> PopPacket() override {
121 if (loss_cadence_ != 0 && (++count_ % loss_cadence_) == 0) {
122 // Pop one extra packet to create the loss.
123 input_->PopPacket();
124 }
125 return input_->PopPacket();
126 }
127
AdvanceOutputEvent()128 void AdvanceOutputEvent() override { return input_->AdvanceOutputEvent(); }
129
ended() const130 bool ended() const override { return input_->ended(); }
131
NextHeader() const132 absl::optional<RTPHeader> NextHeader() const override {
133 return input_->NextHeader();
134 }
135
136 private:
137 const int loss_cadence_;
138 int count_ = 0;
139 const std::unique_ptr<NetEqInput> input_;
140 };
141
142 class AudioChecksumWithOutput : public AudioChecksum {
143 public:
AudioChecksumWithOutput(std::string * output_str)144 explicit AudioChecksumWithOutput(std::string* output_str)
145 : output_str_(*output_str) {}
~AudioChecksumWithOutput()146 ~AudioChecksumWithOutput() { output_str_ = Finish(); }
147
148 private:
149 std::string& output_str_;
150 };
151
RunTest(int loss_cadence,std::string * checksum)152 NetEqNetworkStatistics RunTest(int loss_cadence, std::string* checksum) {
153 NetEq::Config config;
154 config.for_test_no_time_stretching = true;
155
156 // The input is mostly useless. It sends zero-samples to a PCM16b encoder,
157 // but the actual encoded samples will never be used by the decoder in the
158 // test. See below about the decoder.
159 auto generator = std::make_unique<ZeroSampleGenerator>();
160 constexpr int kSampleRateHz = 32000;
161 constexpr int kPayloadType = 100;
162 AudioEncoderPcm16B::Config encoder_config;
163 encoder_config.sample_rate_hz = kSampleRateHz;
164 encoder_config.payload_type = kPayloadType;
165 auto encoder = std::make_unique<AudioEncoderPcm16B>(encoder_config);
166 constexpr int kRunTimeMs = 10000;
167 auto input = std::make_unique<EncodeNetEqInput>(
168 std::move(generator), std::move(encoder), kRunTimeMs);
169 // Wrap the input in a loss function.
170 auto lossy_input =
171 std::make_unique<LossyInput>(loss_cadence, std::move(input));
172
173 // Settinng up decoders.
174 NetEqTest::DecoderMap decoders;
175 // Using a fake decoder which simply reads the output audio from a file.
176 auto input_file = std::make_unique<InputAudioFile>(
177 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"));
178 AudioDecoderPlc dec(std::move(input_file), kSampleRateHz);
179 // Masquerading as a PCM16b decoder.
180 decoders.emplace(kPayloadType, SdpAudioFormat("l16", 32000, 1));
181
182 // Output is simply a checksum calculator.
183 auto output = std::make_unique<AudioChecksumWithOutput>(checksum);
184
185 // No callback objects.
186 NetEqTest::Callbacks callbacks;
187
188 NetEqTest neteq_test(
189 config, /*decoder_factory=*/
190 new rtc::RefCountedObject<test::AudioDecoderProxyFactory>(&dec),
191 /*codecs=*/decoders, /*text_log=*/nullptr, /*neteq_factory=*/nullptr,
192 /*input=*/std::move(lossy_input), std::move(output), callbacks);
193 EXPECT_LE(kRunTimeMs, neteq_test.Run());
194
195 auto lifetime_stats = neteq_test.LifetimeStats();
196 EXPECT_EQ(dec.concealed_samples(), lifetime_stats.concealed_samples);
197 EXPECT_EQ(dec.concealment_events(), lifetime_stats.concealment_events);
198
199 return neteq_test.SimulationStats();
200 }
201 } // namespace
202
TEST(NetEqDecoderPlc,Test)203 TEST(NetEqDecoderPlc, Test) {
204 std::string checksum;
205 auto stats = RunTest(10, &checksum);
206
207 std::string checksum_no_loss;
208 auto stats_no_loss = RunTest(0, &checksum_no_loss);
209
210 EXPECT_EQ(checksum, checksum_no_loss);
211
212 EXPECT_EQ(stats.preemptive_rate, stats_no_loss.preemptive_rate);
213 EXPECT_EQ(stats.accelerate_rate, stats_no_loss.accelerate_rate);
214 EXPECT_EQ(0, stats_no_loss.expand_rate);
215 EXPECT_GT(stats.expand_rate, 0);
216 }
217
218 } // namespace test
219 } // namespace webrtc
220