1 /*
2 * Copyright (c) 2013 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 <stdio.h>
12 #include <string.h>
13
14 #include <memory>
15
16 #include "common_types.h" // NOLINT(build/include)
17 #include "modules/audio_coding/codecs/audio_format_conversion.h"
18 #include "modules/audio_coding/include/audio_coding_module.h"
19 #include "modules/audio_coding/test/Channel.h"
20 #include "modules/audio_coding/test/PCMFile.h"
21 #include "modules/include/module_common_types.h"
22 #include "rtc_base/flags.h"
23 #include "system_wrappers/include/clock.h"
24 #include "test/gtest.h"
25 #include "test/testsupport/fileutils.h"
26
27 // Codec.
28 DEFINE_string(codec, "opus", "Codec Name");
29 DEFINE_int(codec_sample_rate_hz, 48000, "Sampling rate in Hertz.");
30 DEFINE_int(codec_channels, 1, "Number of channels of the codec.");
31
32 // PCM input/output.
33 DEFINE_string(input, "", "Input PCM file at 16 kHz.");
34 DEFINE_bool(input_stereo, false, "Input is stereo.");
35 DEFINE_int(input_fs_hz, 32000, "Input sample rate Hz.");
36 DEFINE_string(output, "insert_rtp_with_timing_out.pcm", "OutputFile");
37 DEFINE_int(output_fs_hz, 32000, "Output sample rate Hz");
38
39 // Timing files
40 DEFINE_string(seq_num, "seq_num", "Sequence number file.");
41 DEFINE_string(send_ts, "send_timestamp", "Send timestamp file.");
42 DEFINE_string(receive_ts, "last_rec_timestamp", "Receive timestamp file");
43
44 // Delay logging
45 DEFINE_string(delay, "", "Log for delay.");
46
47 // Other setups
48 DEFINE_bool(verbose, false, "Verbosity.");
49 DEFINE_float(loss_rate, 0, "Rate of packet loss < 1");
50
51 DEFINE_bool(help, false, "Prints this message.");
52
53 const int32_t kAudioPlayedOut = 0x00000001;
54 const int32_t kPacketPushedIn = 0x00000001 << 1;
55 const int kPlayoutPeriodMs = 10;
56
57 namespace webrtc {
58
59 class InsertPacketWithTiming {
60 public:
InsertPacketWithTiming()61 InsertPacketWithTiming()
62 : sender_clock_(new SimulatedClock(0)),
63 receiver_clock_(new SimulatedClock(0)),
64 send_acm_(AudioCodingModule::Create(sender_clock_)),
65 receive_acm_(AudioCodingModule::Create(receiver_clock_)),
66 channel_(new Channel),
67 seq_num_fid_(fopen(FLAG_seq_num, "rt")),
68 send_ts_fid_(fopen(FLAG_send_ts, "rt")),
69 receive_ts_fid_(fopen(FLAG_receive_ts, "rt")),
70 pcm_out_fid_(fopen(FLAG_output, "wb")),
71 samples_in_1ms_(48),
72 num_10ms_in_codec_frame_(2), // Typical 20 ms frames.
73 time_to_insert_packet_ms_(3), // An arbitrary offset on pushing packet.
74 next_receive_ts_(0),
75 time_to_playout_audio_ms_(kPlayoutPeriodMs),
76 loss_threshold_(0),
77 playout_timing_fid_(fopen("playout_timing.txt", "wt")) {}
78
SetUp()79 void SetUp() {
80 ASSERT_TRUE(sender_clock_ != NULL);
81 ASSERT_TRUE(receiver_clock_ != NULL);
82
83 ASSERT_TRUE(send_acm_.get() != NULL);
84 ASSERT_TRUE(receive_acm_.get() != NULL);
85 ASSERT_TRUE(channel_ != NULL);
86
87 ASSERT_TRUE(seq_num_fid_ != NULL);
88 ASSERT_TRUE(send_ts_fid_ != NULL);
89 ASSERT_TRUE(receive_ts_fid_ != NULL);
90
91 ASSERT_TRUE(playout_timing_fid_ != NULL);
92
93 next_receive_ts_ = ReceiveTimestamp();
94
95 CodecInst codec;
96 ASSERT_EQ(0, AudioCodingModule::Codec(FLAG_codec, &codec,
97 FLAG_codec_sample_rate_hz,
98 FLAG_codec_channels));
99 ASSERT_EQ(0, receive_acm_->InitializeReceiver());
100 ASSERT_EQ(0, send_acm_->RegisterSendCodec(codec));
101 ASSERT_EQ(true, receive_acm_->RegisterReceiveCodec(codec.pltype,
102 CodecInstToSdp(codec)));
103
104 // Set codec-dependent parameters.
105 samples_in_1ms_ = codec.plfreq / 1000;
106 num_10ms_in_codec_frame_ = codec.pacsize / (codec.plfreq / 100);
107
108 channel_->RegisterReceiverACM(receive_acm_.get());
109 send_acm_->RegisterTransportCallback(channel_);
110
111 if (strlen(FLAG_input) == 0) {
112 std::string file_name = test::ResourcePath("audio_coding/testfile32kHz",
113 "pcm");
114 pcm_in_fid_.Open(file_name, 32000, "r", true); // auto-rewind
115 std::cout << "Input file " << file_name << " 32 kHz mono." << std::endl;
116 } else {
117 pcm_in_fid_.Open(FLAG_input, static_cast<uint16_t>(FLAG_input_fs_hz),
118 "r", true); // auto-rewind
119 std::cout << "Input file " << FLAG_input << "at " << FLAG_input_fs_hz
120 << " Hz in " << ((FLAG_input_stereo) ? "stereo." : "mono.")
121 << std::endl;
122 pcm_in_fid_.ReadStereo(FLAG_input_stereo);
123 }
124
125 ASSERT_TRUE(pcm_out_fid_ != NULL);
126 std::cout << "Output file " << FLAG_output << " at " << FLAG_output_fs_hz
127 << " Hz." << std::endl;
128
129 // Other setups
130 if (FLAG_loss_rate > 0)
131 loss_threshold_ = RAND_MAX * FLAG_loss_rate;
132 else
133 loss_threshold_ = 0;
134 }
135
TickOneMillisecond(uint32_t * action)136 void TickOneMillisecond(uint32_t* action) {
137 // One millisecond passed.
138 time_to_insert_packet_ms_--;
139 time_to_playout_audio_ms_--;
140 sender_clock_->AdvanceTimeMilliseconds(1);
141 receiver_clock_->AdvanceTimeMilliseconds(1);
142
143 // Reset action.
144 *action = 0;
145
146 // Is it time to pull audio?
147 if (time_to_playout_audio_ms_ == 0) {
148 time_to_playout_audio_ms_ = kPlayoutPeriodMs;
149 bool muted;
150 receive_acm_->PlayoutData10Ms(static_cast<int>(FLAG_output_fs_hz),
151 &frame_, &muted);
152 ASSERT_FALSE(muted);
153 fwrite(frame_.data(), sizeof(*frame_.data()),
154 frame_.samples_per_channel_ * frame_.num_channels_, pcm_out_fid_);
155 *action |= kAudioPlayedOut;
156 }
157
158 // Is it time to push in next packet?
159 if (time_to_insert_packet_ms_ <= .5) {
160 *action |= kPacketPushedIn;
161
162 // Update time-to-insert packet.
163 uint32_t t = next_receive_ts_;
164 next_receive_ts_ = ReceiveTimestamp();
165 time_to_insert_packet_ms_ += static_cast<float>(next_receive_ts_ - t) /
166 samples_in_1ms_;
167
168 // Push in just enough audio.
169 for (int n = 0; n < num_10ms_in_codec_frame_; n++) {
170 pcm_in_fid_.Read10MsData(frame_);
171 EXPECT_GE(send_acm_->Add10MsData(frame_), 0);
172 }
173
174 // Set the parameters for the packet to be pushed in receiver ACM right
175 // now.
176 uint32_t ts = SendTimestamp();
177 int seq_num = SequenceNumber();
178 bool lost = false;
179 channel_->set_send_timestamp(ts);
180 channel_->set_sequence_number(seq_num);
181 if (loss_threshold_ > 0 && rand() < loss_threshold_) {
182 channel_->set_num_packets_to_drop(1);
183 lost = true;
184 }
185
186 if (FLAG_verbose) {
187 if (!lost) {
188 std::cout << "\nInserting packet number " << seq_num
189 << " timestamp " << ts << std::endl;
190 } else {
191 std::cout << "\nLost packet number " << seq_num
192 << " timestamp " << ts << std::endl;
193 }
194 }
195 }
196 }
197
TearDown()198 void TearDown() {
199 delete channel_;
200
201 fclose(seq_num_fid_);
202 fclose(send_ts_fid_);
203 fclose(receive_ts_fid_);
204 fclose(pcm_out_fid_);
205 pcm_in_fid_.Close();
206 }
207
~InsertPacketWithTiming()208 ~InsertPacketWithTiming() {
209 delete sender_clock_;
210 delete receiver_clock_;
211 }
212
213 // Are there more info to simulate.
HasPackets()214 bool HasPackets() {
215 if (feof(seq_num_fid_) || feof(send_ts_fid_) || feof(receive_ts_fid_))
216 return false;
217 return true;
218 }
219
220 // Jitter buffer delay.
Delay(int * optimal_delay,int * current_delay)221 void Delay(int* optimal_delay, int* current_delay) {
222 NetworkStatistics statistics;
223 receive_acm_->GetNetworkStatistics(&statistics);
224 *optimal_delay = statistics.preferredBufferSize;
225 *current_delay = statistics.currentBufferSize;
226 }
227
228 private:
SendTimestamp()229 uint32_t SendTimestamp() {
230 uint32_t t;
231 EXPECT_EQ(1, fscanf(send_ts_fid_, "%u\n", &t));
232 return t;
233 }
234
ReceiveTimestamp()235 uint32_t ReceiveTimestamp() {
236 uint32_t t;
237 EXPECT_EQ(1, fscanf(receive_ts_fid_, "%u\n", &t));
238 return t;
239 }
240
SequenceNumber()241 int SequenceNumber() {
242 int n;
243 EXPECT_EQ(1, fscanf(seq_num_fid_, "%d\n", &n));
244 return n;
245 }
246
247 // This class just creates these pointers, not deleting them. They are deleted
248 // by the associated ACM.
249 SimulatedClock* sender_clock_;
250 SimulatedClock* receiver_clock_;
251
252 std::unique_ptr<AudioCodingModule> send_acm_;
253 std::unique_ptr<AudioCodingModule> receive_acm_;
254 Channel* channel_;
255
256 FILE* seq_num_fid_; // Input (text), one sequence number per line.
257 FILE* send_ts_fid_; // Input (text), one send timestamp per line.
258 FILE* receive_ts_fid_; // Input (text), one receive timestamp per line.
259 FILE* pcm_out_fid_; // Output PCM16.
260
261 PCMFile pcm_in_fid_; // Input PCM16.
262
263 int samples_in_1ms_;
264
265 // TODO(turajs): this can be computed from the send timestamp, but there is
266 // some complication to account for lost and reordered packets.
267 int num_10ms_in_codec_frame_;
268
269 float time_to_insert_packet_ms_;
270 uint32_t next_receive_ts_;
271 uint32_t time_to_playout_audio_ms_;
272
273 AudioFrame frame_;
274
275 double loss_threshold_;
276
277 // Output (text), sequence number, playout timestamp, time (ms) of playout,
278 // per line.
279 FILE* playout_timing_fid_;
280 };
281
282 } // webrtc
283
main(int argc,char * argv[])284 int main(int argc, char* argv[]) {
285 if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true)) {
286 return 1;
287 }
288 if (FLAG_help) {
289 rtc::FlagList::Print(nullptr, false);
290 return 0;
291 }
292
293 webrtc::InsertPacketWithTiming test;
294 test.SetUp();
295
296 FILE* delay_log = NULL;
297 if (strlen(FLAG_delay) > 0) {
298 delay_log = fopen(FLAG_delay, "wt");
299 if (delay_log == NULL) {
300 std::cout << "Cannot open the file to log delay values." << std::endl;
301 exit(1);
302 }
303 }
304
305 uint32_t action_taken;
306 int optimal_delay_ms;
307 int current_delay_ms;
308 while (test.HasPackets()) {
309 test.TickOneMillisecond(&action_taken);
310
311 if (action_taken != 0) {
312 test.Delay(&optimal_delay_ms, ¤t_delay_ms);
313 if (delay_log != NULL) {
314 fprintf(delay_log, "%3d %3d\n", optimal_delay_ms, current_delay_ms);
315 }
316 }
317 }
318 std::cout << std::endl;
319 test.TearDown();
320 if (delay_log != NULL)
321 fclose(delay_log);
322 }
323