• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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