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 #include "modules/audio_coding/neteq/tools/neteq_test_factory.h"
12
13 #include <errno.h>
14 #include <limits.h> // For ULONG_MAX returned by strtoul.
15 #include <stdio.h>
16 #include <stdlib.h> // For strtoul.
17
18 #include <fstream>
19 #include <iostream>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <utility>
24
25 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
26 #include "api/neteq/neteq.h"
27 #include "modules/audio_coding/neteq/tools/audio_sink.h"
28 #include "modules/audio_coding/neteq/tools/fake_decode_from_file.h"
29 #include "modules/audio_coding/neteq/tools/initial_packet_inserter_neteq_input.h"
30 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
31 #include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
32 #include "modules/audio_coding/neteq/tools/neteq_event_log_input.h"
33 #include "modules/audio_coding/neteq/tools/neteq_packet_source_input.h"
34 #include "modules/audio_coding/neteq/tools/neteq_replacement_input.h"
35 #include "modules/audio_coding/neteq/tools/neteq_stats_getter.h"
36 #include "modules/audio_coding/neteq/tools/neteq_stats_plotter.h"
37 #include "modules/audio_coding/neteq/tools/neteq_test.h"
38 #include "modules/audio_coding/neteq/tools/output_audio_file.h"
39 #include "modules/audio_coding/neteq/tools/output_wav_file.h"
40 #include "modules/audio_coding/neteq/tools/rtp_file_source.h"
41 #include "rtc_base/checks.h"
42 #include "rtc_base/ref_counted_object.h"
43 #include "test/function_audio_decoder_factory.h"
44 #include "test/testsupport/file_utils.h"
45
46 namespace webrtc {
47 namespace test {
48 namespace {
49
CodecSampleRate(uint8_t payload_type,webrtc::test::NetEqTestFactory::Config config)50 absl::optional<int> CodecSampleRate(
51 uint8_t payload_type,
52 webrtc::test::NetEqTestFactory::Config config) {
53 if (payload_type == config.pcmu || payload_type == config.pcma ||
54 payload_type == config.ilbc || payload_type == config.pcm16b ||
55 payload_type == config.cn_nb || payload_type == config.avt)
56 return 8000;
57 if (payload_type == config.isac || payload_type == config.pcm16b_wb ||
58 payload_type == config.g722 || payload_type == config.cn_wb ||
59 payload_type == config.avt_16)
60 return 16000;
61 if (payload_type == config.isac_swb || payload_type == config.pcm16b_swb32 ||
62 payload_type == config.cn_swb32 || payload_type == config.avt_32)
63 return 32000;
64 if (payload_type == config.opus || payload_type == config.pcm16b_swb48 ||
65 payload_type == config.cn_swb48 || payload_type == config.avt_48)
66 return 48000;
67 if (payload_type == config.red)
68 return 0;
69 return absl::nullopt;
70 }
71
72 } // namespace
73
74 // A callback class which prints whenver the inserted packet stream changes
75 // the SSRC.
76 class SsrcSwitchDetector : public NetEqPostInsertPacket {
77 public:
78 // Takes a pointer to another callback object, which will be invoked after
79 // this object finishes. This does not transfer ownership, and null is a
80 // valid value.
SsrcSwitchDetector(NetEqPostInsertPacket * other_callback)81 explicit SsrcSwitchDetector(NetEqPostInsertPacket* other_callback)
82 : other_callback_(other_callback) {}
83
AfterInsertPacket(const NetEqInput::PacketData & packet,NetEq * neteq)84 void AfterInsertPacket(const NetEqInput::PacketData& packet,
85 NetEq* neteq) override {
86 if (last_ssrc_ && packet.header.ssrc != *last_ssrc_) {
87 std::cout << "Changing streams from 0x" << std::hex << *last_ssrc_
88 << " to 0x" << std::hex << packet.header.ssrc << std::dec
89 << " (payload type "
90 << static_cast<int>(packet.header.payloadType) << ")"
91 << std::endl;
92 }
93 last_ssrc_ = packet.header.ssrc;
94 if (other_callback_) {
95 other_callback_->AfterInsertPacket(packet, neteq);
96 }
97 }
98
99 private:
100 NetEqPostInsertPacket* other_callback_;
101 absl::optional<uint32_t> last_ssrc_;
102 };
103
104 NetEqTestFactory::NetEqTestFactory() = default;
105 NetEqTestFactory::~NetEqTestFactory() = default;
106
107 NetEqTestFactory::Config::Config() = default;
108 NetEqTestFactory::Config::Config(const Config& other) = default;
109 NetEqTestFactory::Config::~Config() = default;
110
InitializeTestFromString(const std::string & input_string,NetEqFactory * factory,const Config & config)111 std::unique_ptr<NetEqTest> NetEqTestFactory::InitializeTestFromString(
112 const std::string& input_string,
113 NetEqFactory* factory,
114 const Config& config) {
115 std::unique_ptr<NetEqInput> input(
116 NetEqEventLogInput::CreateFromString(input_string, config.ssrc_filter));
117 if (!input) {
118 std::cerr << "Error: Cannot parse input string" << std::endl;
119 return nullptr;
120 }
121 return InitializeTest(std::move(input), factory, config);
122 }
123
InitializeTestFromFile(const std::string & input_file_name,NetEqFactory * factory,const Config & config)124 std::unique_ptr<NetEqTest> NetEqTestFactory::InitializeTestFromFile(
125 const std::string& input_file_name,
126 NetEqFactory* factory,
127 const Config& config) {
128 // Gather RTP header extensions in a map.
129 NetEqPacketSourceInput::RtpHeaderExtensionMap rtp_ext_map = {
130 {config.audio_level, kRtpExtensionAudioLevel},
131 {config.abs_send_time, kRtpExtensionAbsoluteSendTime},
132 {config.transport_seq_no, kRtpExtensionTransportSequenceNumber},
133 {config.video_content_type, kRtpExtensionVideoContentType},
134 {config.video_timing, kRtpExtensionVideoTiming}};
135
136 std::unique_ptr<NetEqInput> input;
137 if (RtpFileSource::ValidRtpDump(input_file_name) ||
138 RtpFileSource::ValidPcap(input_file_name)) {
139 input.reset(new NetEqRtpDumpInput(input_file_name, rtp_ext_map,
140 config.ssrc_filter));
141 } else {
142 input.reset(NetEqEventLogInput::CreateFromFile(input_file_name,
143 config.ssrc_filter));
144 }
145
146 std::cout << "Input file: " << input_file_name << std::endl;
147 if (!input) {
148 std::cerr << "Error: Cannot open input file" << std::endl;
149 return nullptr;
150 }
151 return InitializeTest(std::move(input), factory, config);
152 }
153
InitializeTest(std::unique_ptr<NetEqInput> input,NetEqFactory * factory,const Config & config)154 std::unique_ptr<NetEqTest> NetEqTestFactory::InitializeTest(
155 std::unique_ptr<NetEqInput> input,
156 NetEqFactory* factory,
157 const Config& config) {
158 if (input->ended()) {
159 std::cerr << "Error: Input is empty" << std::endl;
160 return nullptr;
161 }
162
163 if (!config.field_trial_string.empty()) {
164 field_trials_ =
165 std::make_unique<ScopedFieldTrials>(config.field_trial_string);
166 }
167
168 // Skip some initial events/packets if requested.
169 if (config.skip_get_audio_events > 0) {
170 std::cout << "Skipping " << config.skip_get_audio_events
171 << " get_audio events" << std::endl;
172 if (!input->NextPacketTime() || !input->NextOutputEventTime()) {
173 std::cerr << "No events found" << std::endl;
174 return nullptr;
175 }
176 for (int i = 0; i < config.skip_get_audio_events; i++) {
177 input->AdvanceOutputEvent();
178 if (!input->NextOutputEventTime()) {
179 std::cerr << "Not enough get_audio events found" << std::endl;
180 return nullptr;
181 }
182 }
183 while (*input->NextPacketTime() < *input->NextOutputEventTime()) {
184 input->PopPacket();
185 if (!input->NextPacketTime()) {
186 std::cerr << "Not enough incoming packets found" << std::endl;
187 return nullptr;
188 }
189 }
190 }
191
192 // Check the sample rate.
193 absl::optional<int> sample_rate_hz;
194 std::set<std::pair<int, uint32_t>> discarded_pt_and_ssrc;
195 while (absl::optional<RTPHeader> first_rtp_header = input->NextHeader()) {
196 RTC_DCHECK(first_rtp_header);
197 sample_rate_hz = CodecSampleRate(first_rtp_header->payloadType, config);
198 if (sample_rate_hz) {
199 std::cout << "Found valid packet with payload type "
200 << static_cast<int>(first_rtp_header->payloadType)
201 << " and SSRC 0x" << std::hex << first_rtp_header->ssrc
202 << std::dec << std::endl;
203 if (config.initial_dummy_packets > 0) {
204 std::cout << "Nr of initial dummy packets: "
205 << config.initial_dummy_packets << std::endl;
206 input = std::make_unique<InitialPacketInserterNetEqInput>(
207 std::move(input), config.initial_dummy_packets, *sample_rate_hz);
208 }
209 break;
210 }
211 // Discard this packet and move to the next. Keep track of discarded payload
212 // types and SSRCs.
213 discarded_pt_and_ssrc.emplace(first_rtp_header->payloadType,
214 first_rtp_header->ssrc);
215 input->PopPacket();
216 }
217 if (!discarded_pt_and_ssrc.empty()) {
218 std::cout << "Discarded initial packets with the following payload types "
219 "and SSRCs:"
220 << std::endl;
221 for (const auto& d : discarded_pt_and_ssrc) {
222 std::cout << "PT " << d.first << "; SSRC 0x" << std::hex
223 << static_cast<int>(d.second) << std::dec << std::endl;
224 }
225 }
226 if (!sample_rate_hz) {
227 std::cerr << "Cannot find any packets with known payload types"
228 << std::endl;
229 return nullptr;
230 }
231
232 // If an output file is requested, open it.
233 std::unique_ptr<AudioSink> output;
234 if (!config.output_audio_filename.has_value()) {
235 output = std::make_unique<VoidAudioSink>();
236 std::cout << "No output audio file" << std::endl;
237 } else if (config.output_audio_filename->size() >= 4 &&
238 config.output_audio_filename->substr(
239 config.output_audio_filename->size() - 4) == ".wav") {
240 // Open a wav file with the known sample rate.
241 output = std::make_unique<OutputWavFile>(*config.output_audio_filename,
242 *sample_rate_hz);
243 std::cout << "Output WAV file: " << *config.output_audio_filename
244 << std::endl;
245 } else {
246 // Open a pcm file.
247 output = std::make_unique<OutputAudioFile>(*config.output_audio_filename);
248 std::cout << "Output PCM file: " << *config.output_audio_filename
249 << std::endl;
250 }
251
252 NetEqTest::DecoderMap codecs = NetEqTest::StandardDecoderMap();
253
254 rtc::scoped_refptr<AudioDecoderFactory> decoder_factory =
255 CreateBuiltinAudioDecoderFactory();
256
257 // Check if a replacement audio file was provided.
258 if (config.replacement_audio_file.size() > 0) {
259 // Find largest unused payload type.
260 int replacement_pt = 127;
261 while (codecs.find(replacement_pt) != codecs.end()) {
262 --replacement_pt;
263 if (replacement_pt <= 0) {
264 std::cerr << "Error: Unable to find available replacement payload type"
265 << std::endl;
266 return nullptr;
267 }
268 }
269
270 auto std_set_int32_to_uint8 = [](const std::set<int32_t>& a) {
271 std::set<uint8_t> b;
272 for (auto& x : a) {
273 b.insert(static_cast<uint8_t>(x));
274 }
275 return b;
276 };
277
278 std::set<uint8_t> cn_types = std_set_int32_to_uint8(
279 {config.cn_nb, config.cn_wb, config.cn_swb32, config.cn_swb48});
280 std::set<uint8_t> forbidden_types =
281 std_set_int32_to_uint8({config.g722, config.red, config.avt,
282 config.avt_16, config.avt_32, config.avt_48});
283 input.reset(new NetEqReplacementInput(std::move(input), replacement_pt,
284 cn_types, forbidden_types));
285
286 // Note that capture-by-copy implies that the lambda captures the value of
287 // decoder_factory before it's reassigned on the left-hand side.
288 decoder_factory = new rtc::RefCountedObject<FunctionAudioDecoderFactory>(
289 [decoder_factory, config](
290 const SdpAudioFormat& format,
291 absl::optional<AudioCodecPairId> codec_pair_id) {
292 std::unique_ptr<AudioDecoder> decoder =
293 decoder_factory->MakeAudioDecoder(format, codec_pair_id);
294 if (!decoder && format.name == "replacement") {
295 decoder = std::make_unique<FakeDecodeFromFile>(
296 std::make_unique<InputAudioFile>(config.replacement_audio_file),
297 format.clockrate_hz, format.num_channels > 1);
298 }
299 return decoder;
300 });
301
302 if (!codecs
303 .insert({replacement_pt, SdpAudioFormat("replacement", 48000, 1)})
304 .second) {
305 std::cerr << "Error: Unable to insert replacement audio codec"
306 << std::endl;
307 return nullptr;
308 }
309 }
310
311 // Create a text log output stream if needed.
312 std::unique_ptr<std::ofstream> text_log;
313 if (config.textlog && config.textlog_filename.has_value()) {
314 // Write to file.
315 text_log = std::make_unique<std::ofstream>(*config.textlog_filename);
316 } else if (config.textlog) {
317 // Print to stdout.
318 text_log = std::make_unique<std::ofstream>();
319 text_log->basic_ios<char>::rdbuf(std::cout.rdbuf());
320 }
321
322 NetEqTest::Callbacks callbacks;
323 stats_plotter_ = std::make_unique<NetEqStatsPlotter>(
324 config.matlabplot, config.pythonplot, config.concealment_events,
325 config.plot_scripts_basename.value_or(""));
326
327 ssrc_switch_detector_.reset(
328 new SsrcSwitchDetector(stats_plotter_->stats_getter()->delay_analyzer()));
329 callbacks.post_insert_packet = ssrc_switch_detector_.get();
330 callbacks.get_audio_callback = stats_plotter_->stats_getter();
331 callbacks.simulation_ended_callback = stats_plotter_.get();
332 NetEq::Config neteq_config;
333 neteq_config.sample_rate_hz = *sample_rate_hz;
334 neteq_config.max_packets_in_buffer = config.max_nr_packets_in_buffer;
335 neteq_config.enable_fast_accelerate = config.enable_fast_accelerate;
336 return std::make_unique<NetEqTest>(
337 neteq_config, decoder_factory, codecs, std::move(text_log), factory,
338 std::move(input), std::move(output), callbacks);
339 }
340
341 } // namespace test
342 } // namespace webrtc
343