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