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/include/audio_coding_module.h" 18#include "webrtc/modules/audio_coding/test/Channel.h" 19#include "webrtc/modules/audio_coding/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. 25DEFINE_string(codec, "opus", "Codec Name"); 26DEFINE_int32(codec_sample_rate_hz, 48000, "Sampling rate in Hertz."); 27DEFINE_int32(codec_channels, 1, "Number of channels of the codec."); 28 29// PCM input/output. 30DEFINE_string(input, "", "Input PCM file at 16 kHz."); 31DEFINE_bool(input_stereo, false, "Input is stereo."); 32DEFINE_int32(input_fs_hz, 32000, "Input sample rate Hz."); 33DEFINE_string(output, "insert_rtp_with_timing_out.pcm", "OutputFile"); 34DEFINE_int32(output_fs_hz, 32000, "Output sample rate Hz"); 35 36// Timing files 37DEFINE_string(seq_num, "seq_num", "Sequence number file."); 38DEFINE_string(send_ts, "send_timestamp", "Send timestamp file."); 39DEFINE_string(receive_ts, "last_rec_timestamp", "Receive timestamp file"); 40 41// Delay logging 42DEFINE_string(delay, "", "Log for delay."); 43 44// Other setups 45DEFINE_bool(verbose, false, "Verbosity."); 46DEFINE_double(loss_rate, 0, "Rate of packet loss < 1"); 47 48const int32_t kAudioPlayedOut = 0x00000001; 49const int32_t kPacketPushedIn = 0x00000001 << 1; 50const int kPlayoutPeriodMs = 10; 51 52namespace webrtc { 53 54class 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 276int 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} 308