Home | History | Annotate | Download | only in test
      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.
     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, &current_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