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 "gtest/gtest.h" 15 #include "webrtc/common_types.h" 16 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" 17 #include "webrtc/modules/audio_coding/main/test/Channel.h" 18 #include "webrtc/modules/audio_coding/main/test/PCMFile.h" 19 #include "webrtc/modules/interface/module_common_types.h" 20 #include "webrtc/system_wrappers/interface/clock.h" 21 #include "webrtc/system_wrappers/interface/scoped_ptr.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_int32(init_delay, 0, "Initial delay."); 46 DEFINE_bool(verbose, false, "Verbosity."); 47 DEFINE_double(loss_rate, 0, "Rate of packet loss < 1"); 48 49 const int32_t kAudioPlayedOut = 0x00000001; 50 const int32_t kPacketPushedIn = 0x00000001 << 1; 51 const int kPlayoutPeriodMs = 10; 52 53 namespace webrtc { 54 55 class InsertPacketWithTiming { 56 public: 57 InsertPacketWithTiming() 58 : sender_clock_(new SimulatedClock(0)), 59 receiver_clock_(new SimulatedClock(0)), 60 send_acm_(AudioCodingModule::Create(0, sender_clock_)), 61 receive_acm_(AudioCodingModule::Create(0, receiver_clock_)), 62 channel_(new Channel), 63 seq_num_fid_(fopen(FLAGS_seq_num.c_str(), "rt")), 64 send_ts_fid_(fopen(FLAGS_send_ts.c_str(), "rt")), 65 receive_ts_fid_(fopen(FLAGS_receive_ts.c_str(), "rt")), 66 pcm_out_fid_(fopen(FLAGS_output.c_str(), "wb")), 67 samples_in_1ms_(48), 68 num_10ms_in_codec_frame_(2), // Typical 20 ms frames. 69 time_to_insert_packet_ms_(3), // An arbitrary offset on pushing packet. 70 next_receive_ts_(0), 71 time_to_playout_audio_ms_(kPlayoutPeriodMs), 72 loss_threshold_(0), 73 playout_timing_fid_(fopen("playout_timing.txt", "wt")) {} 74 75 void SetUp() { 76 ASSERT_TRUE(sender_clock_ != NULL); 77 ASSERT_TRUE(receiver_clock_ != NULL); 78 79 ASSERT_TRUE(send_acm_.get() != NULL); 80 ASSERT_TRUE(receive_acm_.get() != NULL); 81 ASSERT_TRUE(channel_ != NULL); 82 83 ASSERT_TRUE(seq_num_fid_ != NULL); 84 ASSERT_TRUE(send_ts_fid_ != NULL); 85 ASSERT_TRUE(receive_ts_fid_ != NULL); 86 87 ASSERT_TRUE(playout_timing_fid_ != NULL); 88 89 next_receive_ts_ = ReceiveTimestamp(); 90 91 CodecInst codec; 92 ASSERT_EQ(0, AudioCodingModule::Codec(FLAGS_codec.c_str(), &codec, 93 FLAGS_codec_sample_rate_hz, 94 FLAGS_codec_channels)); 95 ASSERT_EQ(0, receive_acm_->InitializeReceiver()); 96 ASSERT_EQ(0, send_acm_->RegisterSendCodec(codec)); 97 ASSERT_EQ(0, receive_acm_->RegisterReceiveCodec(codec)); 98 99 // Set codec-dependent parameters. 100 samples_in_1ms_ = codec.plfreq / 1000; 101 num_10ms_in_codec_frame_ = codec.pacsize / (codec.plfreq / 100); 102 103 channel_->RegisterReceiverACM(receive_acm_.get()); 104 send_acm_->RegisterTransportCallback(channel_); 105 106 if (FLAGS_input.size() == 0) { 107 std::string file_name = test::ResourcePath("audio_coding/testfile32kHz", 108 "pcm"); 109 pcm_in_fid_.Open(file_name, 32000, "r", true); // auto-rewind 110 std::cout << "Input file " << file_name << " 32 kHz mono." << std::endl; 111 } else { 112 pcm_in_fid_.Open(FLAGS_input, static_cast<uint16_t>(FLAGS_input_fs_hz), 113 "r", true); // auto-rewind 114 std::cout << "Input file " << FLAGS_input << "at " << FLAGS_input_fs_hz 115 << " Hz in " << ((FLAGS_input_stereo) ? "stereo." : "mono.") 116 << std::endl; 117 pcm_in_fid_.ReadStereo(FLAGS_input_stereo); 118 } 119 120 ASSERT_TRUE(pcm_out_fid_ != NULL); 121 std::cout << "Output file " << FLAGS_output << " at " << FLAGS_output_fs_hz 122 << " Hz." << std::endl; 123 124 // Other setups 125 if (FLAGS_init_delay > 0) 126 EXPECT_EQ(0, receive_acm_->SetInitialPlayoutDelay(FLAGS_init_delay)); 127 128 if (FLAGS_loss_rate > 0) 129 loss_threshold_ = RAND_MAX * FLAGS_loss_rate; 130 else 131 loss_threshold_ = 0; 132 } 133 134 void TickOneMillisecond(uint32_t* action) { 135 // One millisecond passed. 136 time_to_insert_packet_ms_--; 137 time_to_playout_audio_ms_--; 138 sender_clock_->AdvanceTimeMilliseconds(1); 139 receiver_clock_->AdvanceTimeMilliseconds(1); 140 141 // Reset action. 142 *action = 0; 143 144 // Is it time to pull audio? 145 if (time_to_playout_audio_ms_ == 0) { 146 time_to_playout_audio_ms_ = kPlayoutPeriodMs; 147 receive_acm_->PlayoutData10Ms(static_cast<int>(FLAGS_output_fs_hz), 148 &frame_); 149 fwrite(frame_.data_, sizeof(frame_.data_[0]), 150 frame_.samples_per_channel_ * frame_.num_channels_, pcm_out_fid_); 151 *action |= kAudioPlayedOut; 152 } 153 154 // Is it time to push in next packet? 155 if (time_to_insert_packet_ms_ <= .5) { 156 *action |= kPacketPushedIn; 157 158 // Update time-to-insert packet. 159 uint32_t t = next_receive_ts_; 160 next_receive_ts_ = ReceiveTimestamp(); 161 time_to_insert_packet_ms_ += static_cast<float>(next_receive_ts_ - t) / 162 samples_in_1ms_; 163 164 // Push in just enough audio. 165 for (int n = 0; n < num_10ms_in_codec_frame_; n++) { 166 pcm_in_fid_.Read10MsData(frame_); 167 EXPECT_EQ(0, send_acm_->Add10MsData(frame_)); 168 } 169 170 // Set the parameters for the packet to be pushed in receiver ACM right 171 // now. 172 uint32_t ts = SendTimestamp(); 173 int seq_num = SequenceNumber(); 174 bool lost = false; 175 channel_->set_send_timestamp(ts); 176 channel_->set_sequence_number(seq_num); 177 if (loss_threshold_ > 0 && rand() < loss_threshold_) { 178 channel_->set_num_packets_to_drop(1); 179 lost = true; 180 } 181 182 // Process audio in send ACM, this should result in generation of a 183 // packet. 184 EXPECT_GT(send_acm_->Process(), 0); 185 186 if (FLAGS_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 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 208 ~InsertPacketWithTiming() { 209 delete sender_clock_; 210 delete receiver_clock_; 211 } 212 213 // Are there more info to simulate. 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. 221 void Delay(int* optimal_delay, int* current_delay) { 222 ACMNetworkStatistics statistics; 223 receive_acm_->NetworkStatistics(&statistics); 224 *optimal_delay = statistics.preferredBufferSize; 225 *current_delay = statistics.currentBufferSize; 226 } 227 228 private: 229 uint32_t SendTimestamp() { 230 uint32_t t; 231 EXPECT_EQ(1, fscanf(send_ts_fid_, "%u\n", &t)); 232 return t; 233 } 234 235 uint32_t ReceiveTimestamp() { 236 uint32_t t; 237 EXPECT_EQ(1, fscanf(receive_ts_fid_, "%u\n", &t)); 238 return t; 239 } 240 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 scoped_ptr<AudioCodingModule> send_acm_; 253 scoped_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 284 int main(int argc, char* argv[]) { 285 google::ParseCommandLineFlags(&argc, &argv, true); 286 webrtc::InsertPacketWithTiming test; 287 test.SetUp(); 288 289 FILE* delay_log = NULL; 290 if (FLAGS_delay.size() > 0) { 291 delay_log = fopen(FLAGS_delay.c_str(), "wt"); 292 if (delay_log == NULL) { 293 std::cout << "Cannot open the file to log delay values." << std::endl; 294 exit(1); 295 } 296 } 297 298 uint32_t action_taken; 299 int optimal_delay_ms; 300 int current_delay_ms; 301 while (test.HasPackets()) { 302 test.TickOneMillisecond(&action_taken); 303 304 if (action_taken != 0) { 305 test.Delay(&optimal_delay_ms, ¤t_delay_ms); 306 if (delay_log != NULL) { 307 fprintf(delay_log, "%3d %3d\n", optimal_delay_ms, current_delay_ms); 308 } 309 } 310 } 311 std::cout << std::endl; 312 test.TearDown(); 313 if (delay_log != NULL) 314 fclose(delay_log); 315 } 316