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 "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, &current_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