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