Home | History | Annotate | Download | only in primitives
      1 /*
      2  *  Copyright (c) 2012 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 <assert.h>
     12 #include <math.h>
     13 
     14 #include <sstream>
     15 #include <string>
     16 
     17 #include "webrtc/modules/video_capture/include/video_capture_factory.h"
     18 #include "webrtc/system_wrappers/interface/tick_util.h"
     19 #include "webrtc/test/testsupport/fileutils.h"
     20 #include "webrtc/test/testsupport/frame_reader.h"
     21 #include "webrtc/test/testsupport/frame_writer.h"
     22 #include "webrtc/test/testsupport/perf_test.h"
     23 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h"
     24 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h"
     25 #include "webrtc/video_engine/test/auto_test/primitives/framedrop_primitives.h"
     26 #include "webrtc/video_engine/test/auto_test/primitives/general_primitives.h"
     27 #include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h"
     28 #include "webrtc/video_engine/test/libvietest/include/tb_interfaces.h"
     29 #include "webrtc/video_engine/test/libvietest/include/vie_external_render_filter.h"
     30 #include "webrtc/video_engine/test/libvietest/include/vie_to_file_renderer.h"
     31 
     32 enum { kWaitTimeForFinalDecodeMs = 100 };
     33 
     34 // Writes the frames to be encoded to file and tracks which frames are sent in
     35 // external transport on the local side and reports them to the
     36 // FrameDropDetector class.
     37 class LocalRendererEffectFilter : public webrtc::ExternalRendererEffectFilter {
     38  public:
     39   LocalRendererEffectFilter(webrtc::ExternalRenderer* renderer,
     40                             FrameDropDetector* frame_drop_detector)
     41       : ExternalRendererEffectFilter(renderer),
     42         frame_drop_detector_(frame_drop_detector) {}
     43   int Transform(int size,
     44                 unsigned char* frame_buffer,
     45                 int64_t ntp_time_ms,
     46                 unsigned int timestamp,
     47                 unsigned int width,
     48                 unsigned int height) {
     49     frame_drop_detector_->ReportFrameState(
     50         FrameDropDetector::kCreated,
     51         timestamp,
     52         webrtc::TickTime::MicrosecondTimestamp());
     53     return webrtc::ExternalRendererEffectFilter::Transform(
     54         size, frame_buffer, ntp_time_ms, timestamp, width, height);
     55   }
     56  private:
     57   FrameDropDetector* frame_drop_detector_;
     58 };
     59 
     60 // Tracks which frames are sent in external transport on the local side
     61 // and reports them to the FrameDropDetector class.
     62 class FrameSentCallback : public SendFrameCallback {
     63  public:
     64   explicit FrameSentCallback(FrameDropDetector* frame_drop_detector)
     65       : frame_drop_detector_(frame_drop_detector) {}
     66   virtual ~FrameSentCallback() {}
     67   virtual void FrameSent(unsigned int rtp_timestamp) {
     68     frame_drop_detector_->ReportFrameState(
     69         FrameDropDetector::kSent,
     70         rtp_timestamp,
     71         webrtc::TickTime::MicrosecondTimestamp());
     72   }
     73 
     74  private:
     75   FrameDropDetector* frame_drop_detector_;
     76 };
     77 
     78 // Tracks which frames are received in external transport on the remote side
     79 // and reports them to the FrameDropDetector class.
     80 class FrameReceivedCallback : public ReceiveFrameCallback {
     81  public:
     82   explicit FrameReceivedCallback(FrameDropDetector* frame_drop_detector)
     83       : frame_drop_detector_(frame_drop_detector) {}
     84   virtual ~FrameReceivedCallback() {}
     85   virtual void FrameReceived(unsigned int rtp_timestamp) {
     86     frame_drop_detector_->ReportFrameState(
     87         FrameDropDetector::kReceived,
     88         rtp_timestamp,
     89         webrtc::TickTime::MicrosecondTimestamp());
     90   }
     91 
     92  private:
     93   FrameDropDetector* frame_drop_detector_;
     94 };
     95 
     96 // Tracks when frames are decoded on the remote side (received from the
     97 // jitter buffer) and reports them to the FrameDropDetector class.
     98 class DecodedTimestampEffectFilter : public webrtc::ViEEffectFilter {
     99  public:
    100   explicit DecodedTimestampEffectFilter(FrameDropDetector* frame_drop_detector)
    101       : frame_drop_detector_(frame_drop_detector) {}
    102   virtual ~DecodedTimestampEffectFilter() {}
    103   virtual int Transform(int size,
    104                         unsigned char* frame_buffer,
    105                         int64_t ntp_time_ms,
    106                         unsigned int timestamp,
    107                         unsigned int width,
    108                         unsigned int height) {
    109     frame_drop_detector_->ReportFrameState(
    110         FrameDropDetector::kDecoded,
    111         timestamp,
    112         webrtc::TickTime::MicrosecondTimestamp());
    113     return 0;
    114   }
    115 
    116  private:
    117   FrameDropDetector* frame_drop_detector_;
    118 };
    119 
    120 class Statistics {
    121  public:
    122   Statistics() : sum_(0.0f), sum_squared_(0.0f), count_(0) {};
    123 
    124   void AddSample(float sample) {
    125     sum_ += sample;
    126     sum_squared_ += sample * sample;
    127     ++count_;
    128   }
    129 
    130   float Mean() {
    131     if (count_ == 0)
    132       return -1.0f;
    133     return sum_ / count_;
    134   }
    135 
    136   float Variance() {
    137     if (count_ == 0)
    138       return -1.0f;
    139     return  sum_squared_ / count_ - Mean() * Mean();
    140   }
    141 
    142   std::string AsString() {
    143     std::stringstream ss;
    144     ss << (Mean() >= 0 ? Mean() : -1) << ", " <<
    145         (Variance() >= 0 ? sqrt(Variance()) : -1);
    146     return ss.str();
    147   }
    148 
    149  private:
    150   float sum_;
    151   float sum_squared_;
    152   int count_;
    153 };
    154 
    155 void TestFullStack(const TbInterfaces& interfaces,
    156                    int capture_id,
    157                    int video_channel,
    158                    int width,
    159                    int height,
    160                    int bit_rate_kbps,
    161                    const NetworkParameters& network,
    162                    FrameDropDetector* frame_drop_detector,
    163                    ViEToFileRenderer* remote_file_renderer,
    164                    ViEToFileRenderer* local_file_renderer) {
    165   webrtc::VideoEngine *video_engine_interface = interfaces.video_engine;
    166   webrtc::ViEBase *base_interface = interfaces.base;
    167   webrtc::ViECapture *capture_interface = interfaces.capture;
    168   webrtc::ViERender *render_interface = interfaces.render;
    169   webrtc::ViECodec *codec_interface = interfaces.codec;
    170   webrtc::ViENetwork *network_interface = interfaces.network;
    171 
    172   // ***************************************************************
    173   // Engine ready. Begin testing class
    174   // ***************************************************************
    175   webrtc::VideoCodec video_codec;
    176   memset(&video_codec, 0, sizeof (webrtc::VideoCodec));
    177 
    178   // Set up all receive codecs. This basically setup the codec interface
    179   // to be able to recognize all receive codecs based on payload type.
    180   for (int idx = 0; idx < codec_interface->NumberOfCodecs(); idx++) {
    181     EXPECT_EQ(0, codec_interface->GetCodec(idx, video_codec));
    182     SetSuitableResolution(&video_codec, width, height);
    183 
    184     EXPECT_EQ(0, codec_interface->SetReceiveCodec(video_channel, video_codec));
    185   }
    186 
    187   // Configure External transport to simulate network interference:
    188   TbExternalTransport external_transport(*interfaces.network, video_channel,
    189                                          NULL);
    190   external_transport.SetNetworkParameters(network);
    191 
    192   FrameSentCallback frame_sent_callback(frame_drop_detector);
    193   FrameReceivedCallback frame_received_callback(frame_drop_detector);
    194   external_transport.RegisterSendFrameCallback(&frame_sent_callback);
    195   external_transport.RegisterReceiveFrameCallback(&frame_received_callback);
    196   EXPECT_EQ(0, network_interface->RegisterSendTransport(video_channel,
    197                                                         external_transport));
    198   RenderToFile(interfaces.render, video_channel, remote_file_renderer);
    199   EXPECT_EQ(0, base_interface->StartReceive(video_channel));
    200 
    201   // Setup only the VP8 codec, which is what we'll use.
    202   webrtc::VideoCodec codec;
    203   EXPECT_TRUE(FindSpecificCodec(webrtc::kVideoCodecVP8, codec_interface,
    204                                 &codec));
    205   codec.startBitrate = bit_rate_kbps;
    206   codec.maxBitrate = bit_rate_kbps;
    207   codec.width = width;
    208   codec.height = height;
    209   EXPECT_EQ(0, codec_interface->SetSendCodec(video_channel, codec));
    210 
    211   webrtc::ViEImageProcess *image_process =
    212       webrtc::ViEImageProcess::GetInterface(video_engine_interface);
    213   EXPECT_TRUE(image_process);
    214 
    215   // Setup the effect filters.
    216   // Local rendering at the send-side is done in an effect filter to avoid
    217   // synchronization issues with the remote renderer.
    218   LocalRendererEffectFilter local_renderer_filter(local_file_renderer,
    219                                                   frame_drop_detector);
    220   EXPECT_EQ(0, image_process->RegisterSendEffectFilter(video_channel,
    221                                                        local_renderer_filter));
    222   DecodedTimestampEffectFilter decode_filter(frame_drop_detector);
    223   EXPECT_EQ(0, image_process->RegisterRenderEffectFilter(video_channel,
    224                                                          decode_filter));
    225   // Send video.
    226   EXPECT_EQ(0, base_interface->StartSend(video_channel));
    227   AutoTestSleep(kAutoTestFullStackSleepTimeMs);
    228 
    229   ViETest::Log("Done!");
    230 
    231   // ***************************************************************
    232   // Testing finished. Tear down Video Engine
    233   // ***************************************************************
    234   EXPECT_EQ(0, capture_interface->DisconnectCaptureDevice(video_channel));
    235 
    236   const int one_way_delay_99_percentile = network.mean_one_way_delay  +
    237         3 * network.std_dev_one_way_delay;
    238 
    239   // Wait for the last packet to arrive before we tear down the receiver.
    240   AutoTestSleep(2 * one_way_delay_99_percentile);
    241   EXPECT_EQ(0, base_interface->StopSend(video_channel));
    242   while (!external_transport.EmptyQueue()) {
    243     AutoTestSleep(one_way_delay_99_percentile);
    244   }
    245   EXPECT_EQ(0, base_interface->StopReceive(video_channel));
    246   EXPECT_EQ(0, network_interface->DeregisterSendTransport(video_channel));
    247   // Wait for the last frame to be decoded and rendered. There is no guarantee
    248   // this wait time will be long enough. Ideally we would wait for at least one
    249   // "receive-side delay", which is what the video coding module calculates
    250   // based on network statistics etc. We don't have access to that value here.
    251   AutoTestSleep(kWaitTimeForFinalDecodeMs);
    252   // Must stop the frame drop detectors in the right order to avoid getting
    253   // frames which for instance are rendered but not decoded.
    254   EXPECT_EQ(0, render_interface->StopRender(video_channel));
    255   EXPECT_EQ(0, render_interface->RemoveRenderer(video_channel));
    256   EXPECT_EQ(0, image_process->DeregisterRenderEffectFilter(video_channel));
    257   EXPECT_EQ(0, image_process->DeregisterSendEffectFilter(video_channel));
    258   image_process->Release();
    259   EXPECT_EQ(0, base_interface->DeleteChannel(video_channel));
    260 
    261   // Collect transport statistics.
    262   int32_t num_rtp_packets = 0;
    263   int32_t num_dropped_packets = 0;
    264   int32_t num_rtcp_packets = 0;
    265   std::map<uint8_t, int> packet_counters;
    266   external_transport.GetStats(num_rtp_packets, num_dropped_packets,
    267                               num_rtcp_packets, &packet_counters);
    268   ViETest::Log("RTP packets    : %5d", num_rtp_packets);
    269   ViETest::Log("Dropped packets: %5d", num_dropped_packets);
    270   ViETest::Log("RTCP packets   : %5d", num_rtcp_packets);
    271 }
    272 
    273 void FixOutputFileForComparison(const std::string& output_file,
    274                                 int frame_length_in_bytes,
    275                                 const std::vector<Frame*>& frames) {
    276   webrtc::test::FrameReaderImpl frame_reader(output_file,
    277                                              frame_length_in_bytes);
    278   const std::string temp_file = output_file + ".fixed";
    279   webrtc::test::FrameWriterImpl frame_writer(temp_file, frame_length_in_bytes);
    280   frame_reader.Init();
    281   frame_writer.Init();
    282 
    283   ASSERT_FALSE(frames.front()->dropped_at_render) << "It should not be "
    284       "possible to drop the first frame. Both because we don't have anything "
    285       "useful to fill that gap with and it is impossible to detect it without "
    286       "any previous timestamps to compare with.";
    287 
    288   uint8_t* last_frame_data = new uint8_t[frame_length_in_bytes];
    289 
    290   // Process the file and write frame duplicates for all dropped frames.
    291   for (std::vector<Frame*>::const_iterator it = frames.begin();
    292        it != frames.end(); ++it) {
    293     if ((*it)->dropped_at_render) {
    294       // Write the previous frame to the output file:
    295       EXPECT_TRUE(frame_writer.WriteFrame(last_frame_data));
    296     } else {
    297       EXPECT_TRUE(frame_reader.ReadFrame(last_frame_data));
    298       EXPECT_TRUE(frame_writer.WriteFrame(last_frame_data));
    299     }
    300   }
    301   delete[] last_frame_data;
    302   frame_reader.Close();
    303   frame_writer.Close();
    304   ASSERT_EQ(0, remove(output_file.c_str()));
    305   ASSERT_EQ(0, rename(temp_file.c_str(), output_file.c_str()));
    306 }
    307 
    308 void FrameDropDetector::ReportFrameState(State state, unsigned int timestamp,
    309                                          int64_t report_time_us) {
    310   dirty_ = true;
    311   switch (state) {
    312     case kCreated: {
    313       int number = created_frames_vector_.size();
    314       Frame* frame = new Frame(number, timestamp);
    315       frame->created_timestamp_in_us_ = report_time_us;
    316       created_frames_vector_.push_back(frame);
    317       created_frames_[timestamp] = frame;
    318       num_created_frames_++;
    319       break;
    320     }
    321     case kSent:
    322       sent_frames_[timestamp] = report_time_us;
    323       if (timestamp_diff_ == 0) {
    324         // When the first created frame arrives we calculate the fixed
    325         // difference between the timestamps of the frames entering and leaving
    326         // the encoder. This diff is used to identify the frames from the
    327         // created_frames_ map.
    328         timestamp_diff_ =
    329             timestamp - created_frames_vector_.front()->frame_timestamp_;
    330       }
    331       num_sent_frames_++;
    332       break;
    333     case kReceived:
    334       received_frames_[timestamp] = report_time_us;
    335       num_received_frames_++;
    336       break;
    337     case kDecoded:
    338       decoded_frames_[timestamp] = report_time_us;
    339       num_decoded_frames_++;
    340       break;
    341     case kRendered:
    342       rendered_frames_[timestamp] = report_time_us;
    343       num_rendered_frames_++;
    344       break;
    345   }
    346 }
    347 
    348 void FrameDropDetector::CalculateResults() {
    349   // Fill in all fields of the Frame objects in the created_frames_ map.
    350   // Iterate over the maps from converted timestamps to the arrival timestamps.
    351   std::map<unsigned int, int64_t>::const_iterator it;
    352   for (it = sent_frames_.begin(); it != sent_frames_.end(); ++it) {
    353     unsigned int created_timestamp = it->first - timestamp_diff_;
    354     created_frames_[created_timestamp]->sent_timestamp_in_us_ = it->second;
    355   }
    356   for (it = received_frames_.begin(); it != received_frames_.end(); ++it) {
    357     unsigned int created_timestamp = it->first - timestamp_diff_;
    358     created_frames_[created_timestamp]->received_timestamp_in_us_ = it->second;
    359   }
    360   for (it = decoded_frames_.begin(); it != decoded_frames_.end(); ++it) {
    361     unsigned int created_timestamp = it->first - timestamp_diff_;
    362     created_frames_[created_timestamp]->decoded_timestamp_in_us_ =it->second;
    363   }
    364   for (it = rendered_frames_.begin(); it != rendered_frames_.end(); ++it) {
    365     unsigned int created_timestamp = it->first - timestamp_diff_;
    366     created_frames_[created_timestamp]->rendered_timestamp_in_us_ = it->second;
    367   }
    368   // Find out where the frames were not present in the different states.
    369   dropped_frames_at_send_ = 0;
    370   dropped_frames_at_receive_ = 0;
    371   dropped_frames_at_decode_ = 0;
    372   dropped_frames_at_render_ = 0;
    373   for (std::vector<Frame*>::const_iterator it = created_frames_vector_.begin();
    374        it != created_frames_vector_.end(); ++it) {
    375     int encoded_timestamp = (*it)->frame_timestamp_ + timestamp_diff_;
    376     if (sent_frames_.find(encoded_timestamp) == sent_frames_.end()) {
    377       (*it)->dropped_at_send = true;
    378       dropped_frames_at_send_++;
    379     }
    380     if (received_frames_.find(encoded_timestamp) == received_frames_.end()) {
    381       (*it)->dropped_at_receive = true;
    382       dropped_frames_at_receive_++;
    383     }
    384     if (decoded_frames_.find(encoded_timestamp) == decoded_frames_.end()) {
    385       (*it)->dropped_at_decode = true;
    386       dropped_frames_at_decode_++;
    387     }
    388     if (rendered_frames_.find(encoded_timestamp) == rendered_frames_.end()) {
    389       (*it)->dropped_at_render = true;
    390       dropped_frames_at_render_++;
    391     }
    392   }
    393   dirty_ = false;
    394 }
    395 
    396 void FrameDropDetector::PrintReport(const std::string& test_label) {
    397   assert(!dirty_);
    398   ViETest::Log("Frame Drop Detector report:");
    399   ViETest::Log("  Created  frames: %ld", created_frames_.size());
    400   ViETest::Log("  Sent     frames: %ld", sent_frames_.size());
    401   ViETest::Log("  Received frames: %ld", received_frames_.size());
    402   ViETest::Log("  Decoded  frames: %ld", decoded_frames_.size());
    403   ViETest::Log("  Rendered frames: %ld", rendered_frames_.size());
    404 
    405   // Display all frames and stats for them:
    406   long last_created = 0;
    407   long last_sent = 0;
    408   long last_received = 0;
    409   long last_decoded = 0;
    410   long last_rendered = 0;
    411   ViETest::Log("\nDeltas between sent frames and drop status:");
    412   ViETest::Log("Unit: Microseconds");
    413   ViETest::Log("Frame  Created    Sent    Received Decoded Rendered "
    414       "Dropped at  Dropped at  Dropped at  Dropped at");
    415   ViETest::Log(" nbr    delta     delta    delta    delta   delta   "
    416       " Send?       Receive?    Decode?     Render?");
    417   Statistics rendering_stats;
    418   for (std::vector<Frame*>::const_iterator it = created_frames_vector_.begin();
    419        it != created_frames_vector_.end(); ++it) {
    420     int created_delta =
    421         static_cast<int>((*it)->created_timestamp_in_us_ - last_created);
    422     int sent_delta = (*it)->dropped_at_send ? -1 :
    423         static_cast<int>((*it)->sent_timestamp_in_us_ - last_sent);
    424     int received_delta = (*it)->dropped_at_receive ? -1 :
    425         static_cast<int>((*it)->received_timestamp_in_us_ - last_received);
    426     int decoded_delta = (*it)->dropped_at_decode ? -1 :
    427         static_cast<int>((*it)->decoded_timestamp_in_us_ - last_decoded);
    428     int rendered_delta = (*it)->dropped_at_render ? -1 :
    429         static_cast<int>((*it)->rendered_timestamp_in_us_ - last_rendered);
    430 
    431     // Set values to -1 for the first frame:
    432     if ((*it)->number_ == 0) {
    433       created_delta = -1;
    434       sent_delta = -1;
    435       received_delta = -1;
    436       decoded_delta = -1;
    437       rendered_delta = -1;
    438     }
    439     ViETest::Log("%5d %8d %8d %8d %8d %8d %10s %10s %10s %10s",
    440                  (*it)->number_,
    441                  created_delta,
    442                  sent_delta,
    443                  received_delta,
    444                  decoded_delta,
    445                  rendered_delta,
    446                  (*it)->dropped_at_send ? "DROPPED" : "      ",
    447                  (*it)->dropped_at_receive ? "DROPPED" : "      ",
    448                  (*it)->dropped_at_decode ? "DROPPED" : "      ",
    449                  (*it)->dropped_at_render ? "DROPPED" : "      ");
    450     last_created = (*it)->created_timestamp_in_us_;
    451     if (!(*it)->dropped_at_send) {
    452       last_sent = (*it)->sent_timestamp_in_us_;
    453     }
    454      if (!(*it)->dropped_at_receive) {
    455       last_received = (*it)->received_timestamp_in_us_;
    456     }
    457     if (!(*it)->dropped_at_decode) {
    458       last_decoded = (*it)->decoded_timestamp_in_us_;
    459     }
    460     if (!(*it)->dropped_at_render) {
    461       last_rendered = (*it)->rendered_timestamp_in_us_;
    462       rendering_stats.AddSample(rendered_delta / 1000.0f);
    463     }
    464   }
    465   ViETest::Log("\nLatency between states (-1 means N/A because of drop):");
    466   ViETest::Log("Unit: Microseconds");
    467   ViETest::Log("Frame  Created    Sent      Received   Decoded      Total    "
    468       "   Total");
    469   ViETest::Log(" nbr   ->Sent  ->Received  ->Decoded ->Rendered    latency   "
    470       "  latency");
    471   ViETest::Log("                                               (incl network)"
    472       "(excl network)");
    473   Statistics latency_incl_network_stats;
    474   for (std::vector<Frame*>::const_iterator it = created_frames_vector_.begin();
    475        it != created_frames_vector_.end(); ++it) {
    476     int created_to_sent = (*it)->dropped_at_send ? -1 :
    477         static_cast<int>((*it)->sent_timestamp_in_us_ -
    478                          (*it)->created_timestamp_in_us_);
    479     int sent_to_received = (*it)->dropped_at_receive ? -1 :
    480         static_cast<int>((*it)->received_timestamp_in_us_ -
    481                          (*it)->sent_timestamp_in_us_);
    482     int received_to_decoded = (*it)->dropped_at_decode ? -1 :
    483         static_cast<int>((*it)->decoded_timestamp_in_us_ -
    484                          (*it)->received_timestamp_in_us_);
    485     int decoded_to_render = (*it)->dropped_at_render ? -1 :
    486         static_cast<int>((*it)->rendered_timestamp_in_us_ -
    487                          (*it)->decoded_timestamp_in_us_);
    488     int total_latency_incl_network = (*it)->dropped_at_render ? -1 :
    489         static_cast<int>((*it)->rendered_timestamp_in_us_ -
    490                          (*it)->created_timestamp_in_us_);
    491     int total_latency_excl_network = (*it)->dropped_at_render ? -1 :
    492         static_cast<int>((*it)->rendered_timestamp_in_us_ -
    493                          (*it)->created_timestamp_in_us_ - sent_to_received);
    494     if (total_latency_incl_network >= 0)
    495       latency_incl_network_stats.AddSample(total_latency_incl_network /
    496                                            1000.0f);
    497     ViETest::Log("%5d %9d %9d %9d %9d %12d %12d",
    498                  (*it)->number_,
    499                  created_to_sent,
    500                  sent_to_received,
    501                  received_to_decoded,
    502                  decoded_to_render,
    503                  total_latency_incl_network,
    504                  total_latency_excl_network);
    505   }
    506 
    507   // Plot all measurements in the same graph since they share the same value
    508   // range.
    509   webrtc::test::PrintResultMeanAndError(
    510       "total_delay_incl_network", "", test_label,
    511       latency_incl_network_stats.AsString(), "ms", false);
    512   webrtc::test::PrintResultMeanAndError(
    513       "time_between_rendered_frames", "", test_label,
    514       rendering_stats.AsString(), "ms", false);
    515 
    516 
    517   // Find and print the dropped frames.
    518   ViETest::Log("\nTotal # dropped frames at:");
    519   ViETest::Log("  Send   : %d", dropped_frames_at_send_);
    520   ViETest::Log("  Receive: %d", dropped_frames_at_receive_);
    521   ViETest::Log("  Decode : %d", dropped_frames_at_decode_);
    522   ViETest::Log("  Render : %d", dropped_frames_at_render_);
    523 }
    524 
    525 void FrameDropDetector::PrintDebugDump() {
    526   assert(!dirty_);
    527   ViETest::Log("\nPrintDebugDump: Frame objects:");
    528   ViETest::Log("Frame FrTimeStamp Created       Sent      Received    Decoded"
    529       "    Rendered ");
    530   for (std::vector<Frame*>::const_iterator it = created_frames_vector_.begin();
    531        it != created_frames_vector_.end(); ++it) {
    532     ViETest::Log("%5d %11u %11lld %11lld %11lld %11lld %11lld",
    533                  (*it)->number_,
    534                  (*it)->frame_timestamp_,
    535                  (*it)->created_timestamp_in_us_,
    536                  (*it)->sent_timestamp_in_us_,
    537                  (*it)->received_timestamp_in_us_,
    538                  (*it)->decoded_timestamp_in_us_,
    539                  (*it)->rendered_timestamp_in_us_);
    540   }
    541   std::vector<int> mismatch_frame_num_list;
    542   for (std::vector<Frame*>::const_iterator it = created_frames_vector_.begin();
    543        it != created_frames_vector_.end(); ++it) {
    544     if ((*it)->dropped_at_render != (*it)->dropped_at_decode) {
    545       mismatch_frame_num_list.push_back((*it)->number_);
    546     }
    547   }
    548   if (mismatch_frame_num_list.size() > 0) {
    549     ViETest::Log("\nDecoded/Rendered mismatches:");
    550     ViETest::Log("Frame FrTimeStamp    Created       Sent      Received    "
    551         "Decoded    Rendered ");
    552     for (std::vector<int>::const_iterator it = mismatch_frame_num_list.begin();
    553          it != mismatch_frame_num_list.end(); ++it) {
    554       Frame* frame = created_frames_vector_[*it];
    555       ViETest::Log("%5d %11u %11lld %11lld %11lld %11lld %11lld",
    556                  frame->number_,
    557                  frame->frame_timestamp_,
    558                  frame->created_timestamp_in_us_,
    559                  frame->sent_timestamp_in_us_,
    560                  frame->received_timestamp_in_us_,
    561                  frame->decoded_timestamp_in_us_,
    562                  frame->rendered_timestamp_in_us_);
    563     }
    564   }
    565 
    566   ViETest::Log("\nReportFrameState method invocations:");
    567   ViETest::Log("  Created : %d", num_created_frames_);
    568   ViETest::Log("  Send    : %d", num_sent_frames_);
    569   ViETest::Log("  Received: %d", num_received_frames_);
    570   ViETest::Log("  Decoded : %d", num_decoded_frames_);
    571   ViETest::Log("  Rendered: %d", num_rendered_frames_);
    572 }
    573 
    574 const std::vector<Frame*>& FrameDropDetector::GetAllFrames() {
    575   assert(!dirty_);
    576   return created_frames_vector_;
    577 }
    578 
    579 int FrameDropDetector::GetNumberOfFramesDroppedAt(State state) {
    580   assert(!dirty_);
    581   switch (state) {
    582     case kSent:
    583       return dropped_frames_at_send_;
    584     case kReceived:
    585       return dropped_frames_at_receive_;
    586     case kDecoded:
    587       return dropped_frames_at_decode_;
    588     case kRendered:
    589       return dropped_frames_at_render_;
    590     default:
    591       return 0;
    592   }
    593 }
    594 
    595 int FrameDropMonitoringRemoteFileRenderer::DeliverFrame(
    596     unsigned char *buffer, int buffer_size, uint32_t time_stamp,
    597     int64_t ntp_time_ms, int64_t render_time, void* /*handle*/) {
    598   // |render_time| provides the ideal render time for this frame. If that time
    599   // has already passed we will render it immediately.
    600   int64_t report_render_time_us = render_time * 1000;
    601   int64_t time_now_us = webrtc::TickTime::MicrosecondTimestamp();
    602   if (render_time < (time_now_us + 500) / 1000) {
    603     report_render_time_us = time_now_us;
    604   }
    605   // Register that this frame has been rendered.
    606   frame_drop_detector_->ReportFrameState(FrameDropDetector::kRendered,
    607                                          time_stamp, report_render_time_us);
    608   return ViEToFileRenderer::DeliverFrame(buffer, buffer_size,
    609                                          time_stamp, ntp_time_ms,
    610                                          render_time, NULL);
    611 }
    612 
    613 int FrameDropMonitoringRemoteFileRenderer::FrameSizeChange(
    614     unsigned int width, unsigned int height, unsigned int number_of_streams) {
    615   return ViEToFileRenderer::FrameSizeChange(width, height, number_of_streams);
    616 }
    617