1 /*
2  *  Copyright (c) 2014 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 #include "modules/audio_coding/neteq/tools/neteq_performance_test.h"
12 
13 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
14 #include "common_types.h"  // NOLINT(build/include)
15 #include "modules/audio_coding/codecs/pcm16b/pcm16b.h"
16 #include "modules/audio_coding/neteq/include/neteq.h"
17 #include "modules/audio_coding/neteq/tools/audio_loop.h"
18 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
19 #include "modules/include/module_common_types.h"
20 #include "rtc_base/checks.h"
21 #include "system_wrappers/include/clock.h"
22 #include "test/testsupport/fileutils.h"
23 #include "typedefs.h"  // NOLINT(build/include)
24 
25 using webrtc::NetEq;
26 using webrtc::test::AudioLoop;
27 using webrtc::test::RtpGenerator;
28 
29 namespace webrtc {
30 namespace test {
31 
Run(int runtime_ms,int lossrate,double drift_factor)32 int64_t NetEqPerformanceTest::Run(int runtime_ms,
33                                   int lossrate,
34                                   double drift_factor) {
35   const std::string kInputFileName =
36       webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
37   const int kSampRateHz = 32000;
38   const webrtc::NetEqDecoder kDecoderType =
39       webrtc::NetEqDecoder::kDecoderPCM16Bswb32kHz;
40   const std::string kDecoderName = "pcm16-swb32";
41   const int kPayloadType = 95;
42 
43   // Initialize NetEq instance.
44   NetEq::Config config;
45   config.sample_rate_hz = kSampRateHz;
46   NetEq* neteq = NetEq::Create(config, CreateBuiltinAudioDecoderFactory());
47   // Register decoder in |neteq|.
48   if (neteq->RegisterPayloadType(kDecoderType, kDecoderName, kPayloadType) != 0)
49     return -1;
50 
51   // Set up AudioLoop object.
52   AudioLoop audio_loop;
53   const size_t kMaxLoopLengthSamples = kSampRateHz * 10;  // 10 second loop.
54   const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000;  // 60 ms.
55   if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples,
56                        kInputBlockSizeSamples))
57     return -1;
58 
59   int32_t time_now_ms = 0;
60 
61   // Get first input packet.
62   RTPHeader rtp_header;
63   RtpGenerator rtp_gen(kSampRateHz / 1000);
64   // Start with positive drift first half of simulation.
65   rtp_gen.set_drift_factor(drift_factor);
66   bool drift_flipped = false;
67   int32_t packet_input_time_ms =
68       rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header);
69   auto input_samples = audio_loop.GetNextBlock();
70   if (input_samples.empty())
71     exit(1);
72   uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)];
73   size_t payload_len = WebRtcPcm16b_Encode(input_samples.data(),
74                                            input_samples.size(), input_payload);
75   RTC_CHECK_EQ(sizeof(input_payload), payload_len);
76 
77   // Main loop.
78   webrtc::Clock* clock = webrtc::Clock::GetRealTimeClock();
79   int64_t start_time_ms = clock->TimeInMilliseconds();
80   AudioFrame out_frame;
81   while (time_now_ms < runtime_ms) {
82     while (packet_input_time_ms <= time_now_ms) {
83       // Drop every N packets, where N = FLAG_lossrate.
84       bool lost = false;
85       if (lossrate > 0) {
86         lost = ((rtp_header.sequenceNumber - 1) % lossrate) == 0;
87       }
88       if (!lost) {
89         // Insert packet.
90         int error =
91             neteq->InsertPacket(rtp_header, input_payload,
92                                 packet_input_time_ms * kSampRateHz / 1000);
93         if (error != NetEq::kOK)
94           return -1;
95       }
96 
97       // Get next packet.
98       packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType,
99                                                   kInputBlockSizeSamples,
100                                                   &rtp_header);
101       input_samples = audio_loop.GetNextBlock();
102       if (input_samples.empty())
103         return -1;
104       payload_len = WebRtcPcm16b_Encode(input_samples.data(),
105                                         input_samples.size(), input_payload);
106       assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t));
107     }
108 
109     // Get output audio, but don't do anything with it.
110     bool muted;
111     int error = neteq->GetAudio(&out_frame, &muted);
112     RTC_CHECK(!muted);
113     if (error != NetEq::kOK)
114       return -1;
115 
116     assert(out_frame.samples_per_channel_ ==
117            static_cast<size_t>(kSampRateHz * 10 / 1000));
118 
119     static const int kOutputBlockSizeMs = 10;
120     time_now_ms += kOutputBlockSizeMs;
121     if (time_now_ms >= runtime_ms / 2 && !drift_flipped) {
122       // Apply negative drift second half of simulation.
123       rtp_gen.set_drift_factor(-drift_factor);
124       drift_flipped = true;
125     }
126   }
127   int64_t end_time_ms = clock->TimeInMilliseconds();
128   delete neteq;
129   return end_time_ms - start_time_ms;
130 }
131 
132 }  // namespace test
133 }  // namespace webrtc
134