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