Update to the neteq_rtpplay utility to support RtcEventLog input files.

This CL adds support for simulating neteq using stored RTP packets as well as calls to GetAudio from an RtcEventLog, using the stored timestamps.
The type of the input file is detected automatically.
BUG=webrtc:4741

Review URL: https://codereview.webrtc.org/1316903002

Cr-Commit-Position: refs/heads/master@{#9886}
diff --git a/webrtc/modules/audio_coding/neteq/neteq_tests.gypi b/webrtc/modules/audio_coding/neteq/neteq_tests.gypi
index 4b04092..50ebbd3 100644
--- a/webrtc/modules/audio_coding/neteq/neteq_tests.gypi
+++ b/webrtc/modules/audio_coding/neteq/neteq_tests.gypi
@@ -7,25 +7,43 @@
 # be found in the AUTHORS file in the root of the source tree.
 
 {
+  'conditions': [
+    ['enable_protobuf==1', {
+      'targets': [
+        {
+          'target_name': 'rtc_event_log_source',
+          'type': 'static_library',
+          'dependencies': [
+            '<(webrtc_root)/webrtc.gyp:rtc_event_log',
+            '<(webrtc_root)/webrtc.gyp:rtc_event_log_proto',
+          ],
+          'sources': [
+            'tools/rtc_event_log_source.h',
+            'tools/rtc_event_log_source.cc',
+          ],
+        },
+        {
+          'target_name': 'neteq_rtpplay',
+          'type': 'executable',
+          'dependencies': [
+            '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
+            '<(webrtc_root)/test/test.gyp:test_support_main',
+            'rtc_event_log_source',
+            'neteq',
+            'neteq_unittest_tools',
+            'pcm16b',
+          ],
+          'sources': [
+            'tools/neteq_rtpplay.cc',
+          ],
+          'defines': [
+          ],
+        }, # neteq_rtpplay
+      ],
+    }],
+  ],
   'targets': [
     {
-      'target_name': 'neteq_rtpplay',
-      'type': 'executable',
-      'dependencies': [
-        '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
-        '<(webrtc_root)/test/test.gyp:test_support_main',
-        'neteq',
-        'neteq_unittest_tools',
-        'pcm16b',
-      ],
-      'sources': [
-        'tools/neteq_rtpplay.cc',
-      ],
-      'defines': [
-      ],
-    }, # neteq_rtpplay
-
-    {
       'target_name': 'RTPencode',
       'type': 'executable',
       'dependencies': [
diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
index 1c08078..d421976 100644
--- a/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
+++ b/webrtc/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
@@ -19,6 +19,7 @@
 
 #include <algorithm>
 #include <iostream>
+#include <limits>
 #include <string>
 
 #include "google/gflags.h"
@@ -31,9 +32,11 @@
 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h"
 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h"
 #include "webrtc/modules/audio_coding/neteq/tools/packet.h"
+#include "webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h"
 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h"
 #include "webrtc/modules/interface/module_common_types.h"
 #include "webrtc/system_wrappers/interface/trace.h"
+#include "webrtc/test/rtp_file_reader.h"
 #include "webrtc/test/testsupport/fileutils.h"
 #include "webrtc/typedefs.h"
 
@@ -385,8 +388,30 @@
   }
 
   printf("Input file: %s\n", argv[1]);
-  rtc::scoped_ptr<webrtc::test::RtpFileSource> file_source(
-      webrtc::test::RtpFileSource::Create(argv[1]));
+
+  // TODO(ivoc): Modify the RtpFileSource::Create and RtcEventLogSource::Create
+  //             functions to return a nullptr on failure instead of crashing
+  //             the program.
+
+  // This temporary solution uses a RtpFileReader directly to check if the file
+  // is a valid RtpDump file.
+  bool is_rtp_dump = false;
+  {
+    rtc::scoped_ptr<webrtc::test::RtpFileReader> rtp_reader(
+        webrtc::test::RtpFileReader::Create(
+            webrtc::test::RtpFileReader::kRtpDump, argv[1]));
+    if (rtp_reader)
+      is_rtp_dump = true;
+  }
+  rtc::scoped_ptr<webrtc::test::PacketSource> file_source;
+  webrtc::test::RtcEventLogSource* event_log_source = nullptr;
+  if (is_rtp_dump) {
+    file_source.reset(webrtc::test::RtpFileSource::Create(argv[1]));
+  } else {
+    event_log_source = webrtc::test::RtcEventLogSource::Create(argv[1]);
+    file_source.reset(event_log_source);
+  }
+
   assert(file_source.get());
 
   // Check if an SSRC value was provided.
@@ -414,7 +439,12 @@
     webrtc::Trace::ReturnTrace();
     return 0;
   }
-  bool packet_available = true;
+  if (packet->payload_length_bytes() == 0 && !replace_payload) {
+    std::cerr << "Warning: input file contains header-only packets, but no "
+              << "replacement file is specified." << std::endl;
+    webrtc::Trace::ReturnTrace();
+    return -1;
+  }
 
   // Check the sample rate.
   int sample_rate_hz = CodecSampleRate(packet->header().payloadType);
@@ -476,17 +506,29 @@
 
   // This is the main simulation loop.
   // Set the simulation clock to start immediately with the first packet.
-  int start_time_ms = packet->time_ms();
-  int time_now_ms = packet->time_ms();
-  int next_input_time_ms = time_now_ms;
-  int next_output_time_ms = time_now_ms;
+  int64_t start_time_ms = rtc::checked_cast<int64_t>(packet->time_ms());
+  int64_t time_now_ms = start_time_ms;
+  int64_t next_input_time_ms = time_now_ms;
+  int64_t next_output_time_ms = time_now_ms;
   if (time_now_ms % kOutputBlockSizeMs != 0) {
     // Make sure that next_output_time_ms is rounded up to the next multiple
     // of kOutputBlockSizeMs. (Legacy bit-exactness.)
     next_output_time_ms +=
         kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs;
   }
-  while (packet_available) {
+
+  bool packet_available = true;
+  bool output_event_available = true;
+  if (!is_rtp_dump) {
+    next_output_time_ms = event_log_source->NextAudioOutputEventMs();
+    if (next_output_time_ms == std::numeric_limits<int64_t>::max())
+      output_event_available = false;
+    start_time_ms = time_now_ms =
+        std::min(next_input_time_ms, next_output_time_ms);
+  }
+  while (packet_available || output_event_available) {
+    // Advance time to next event.
+    time_now_ms = std::min(next_input_time_ms, next_output_time_ms);
     // Check if it is time to insert packet.
     while (time_now_ms >= next_input_time_ms && packet_available) {
       assert(packet->virtual_payload_length_bytes() > 0);
@@ -505,11 +547,9 @@
                                      next_packet.get());
         payload_ptr = payload.get();
       }
-      int error =
-          neteq->InsertPacket(rtp_header,
-                              payload_ptr,
-                              payload_len,
-                              packet->time_ms() * sample_rate_hz / 1000);
+      int error = neteq->InsertPacket(
+          rtp_header, payload_ptr, payload_len,
+          static_cast<uint32_t>(packet->time_ms() * sample_rate_hz / 1000));
       if (error != NetEq::kOK) {
         if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) {
           std::cerr << "RTP Payload type "
@@ -535,24 +575,27 @@
       webrtc::test::Packet* temp_packet = file_source->NextPacket();
       if (temp_packet) {
         packet.reset(temp_packet);
+        if (replace_payload) {
+          // At this point |packet| contains the packet *after* |next_packet|.
+          // Swap Packet objects between |packet| and |next_packet|.
+          packet.swap(next_packet);
+          // Swap the status indicators unless they're already the same.
+          if (packet_available != next_packet_available) {
+            packet_available = !packet_available;
+            next_packet_available = !next_packet_available;
+          }
+        }
+        next_input_time_ms = rtc::checked_cast<int64_t>(packet->time_ms());
       } else {
+        // Set next input time to the maximum value of int64_t to prevent the
+        // time_now_ms from becoming stuck at the final value.
+        next_input_time_ms = std::numeric_limits<int64_t>::max();
         packet_available = false;
       }
-      if (replace_payload) {
-        // At this point |packet| contains the packet *after* |next_packet|.
-        // Swap Packet objects between |packet| and |next_packet|.
-        packet.swap(next_packet);
-        // Swap the status indicators unless they're already the same.
-        if (packet_available != next_packet_available) {
-          packet_available = !packet_available;
-          next_packet_available = !next_packet_available;
-        }
-      }
-      next_input_time_ms = packet->time_ms();
     }
 
     // Check if it is time to get output audio.
-    if (time_now_ms >= next_output_time_ms) {
+    while (time_now_ms >= next_output_time_ms && output_event_available) {
       static const size_t kOutDataLen =
           kOutputBlockSizeMs * kMaxSamplesPerMs * kMaxChannels;
       int16_t out_data[kOutDataLen];
@@ -577,14 +620,20 @@
         webrtc::Trace::ReturnTrace();
         exit(1);
       }
-      next_output_time_ms += kOutputBlockSizeMs;
+      if (is_rtp_dump) {
+        next_output_time_ms += kOutputBlockSizeMs;
+        if (!packet_available)
+          output_event_available = false;
+      } else {
+        next_output_time_ms = event_log_source->NextAudioOutputEventMs();
+        if (next_output_time_ms == std::numeric_limits<int64_t>::max())
+          output_event_available = false;
+      }
     }
-    // Advance time to next event.
-    time_now_ms = std::min(next_input_time_ms, next_output_time_ms);
   }
-
   printf("Simulation done\n");
-  printf("Produced %i ms of audio\n", time_now_ms - start_time_ms);
+  printf("Produced %i ms of audio\n",
+         static_cast<int>(time_now_ms - start_time_ms));
 
   delete neteq;
   webrtc::Trace::ReturnTrace();
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.cc b/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.cc
new file mode 100644
index 0000000..c2bccca
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.cc
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h"
+
+#include <assert.h>
+#include <string.h>
+#include <iostream>
+#include <limits>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/modules/audio_coding/neteq/tools/packet.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
+#include "webrtc/video/rtc_event_log.h"
+
+// Files generated at build-time by the protobuf compiler.
+#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
+#include "external/webrtc/webrtc/video/rtc_event_log.pb.h"
+#else
+#include "webrtc/video/rtc_event_log.pb.h"
+#endif
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+const rtclog::RtpPacket* GetRtpPacket(const rtclog::Event& event) {
+  if (!event.has_type() || event.type() != rtclog::Event::RTP_EVENT)
+    return nullptr;
+  if (!event.has_timestamp_us() || !event.has_rtp_packet())
+    return nullptr;
+  const rtclog::RtpPacket& rtp_packet = event.rtp_packet();
+  if (!rtp_packet.has_type() || rtp_packet.type() != rtclog::AUDIO ||
+      !rtp_packet.has_incoming() || !rtp_packet.incoming() ||
+      !rtp_packet.has_packet_length() || rtp_packet.packet_length() == 0 ||
+      !rtp_packet.has_header() || rtp_packet.header().size() == 0 ||
+      rtp_packet.packet_length() < rtp_packet.header().size())
+    return nullptr;
+  return &rtp_packet;
+}
+
+const rtclog::DebugEvent* GetAudioOutputEvent(const rtclog::Event& event) {
+  if (!event.has_type() || event.type() != rtclog::Event::DEBUG_EVENT)
+    return nullptr;
+  if (!event.has_timestamp_us() || !event.has_debug_event())
+    return nullptr;
+  const rtclog::DebugEvent& debug_event = event.debug_event();
+  if (!debug_event.has_type() ||
+      debug_event.type() != rtclog::DebugEvent::AUDIO_PLAYOUT)
+    return nullptr;
+  return &debug_event;
+}
+
+}  // namespace
+
+RtcEventLogSource* RtcEventLogSource::Create(const std::string& file_name) {
+  RtcEventLogSource* source = new RtcEventLogSource();
+  CHECK(source->OpenFile(file_name));
+  return source;
+}
+
+RtcEventLogSource::~RtcEventLogSource() {}
+
+bool RtcEventLogSource::RegisterRtpHeaderExtension(RTPExtensionType type,
+                                                   uint8_t id) {
+  CHECK(parser_.get());
+  return parser_->RegisterRtpHeaderExtension(type, id);
+}
+
+Packet* RtcEventLogSource::NextPacket() {
+  while (rtp_packet_index_ < event_log_->stream_size()) {
+    const rtclog::Event& event = event_log_->stream(rtp_packet_index_);
+    const rtclog::RtpPacket* rtp_packet = GetRtpPacket(event);
+    rtp_packet_index_++;
+    if (rtp_packet) {
+      uint8_t* packet_header = new uint8_t[rtp_packet->header().size()];
+      memcpy(packet_header, rtp_packet->header().data(),
+             rtp_packet->header().size());
+      Packet* packet = new Packet(packet_header, rtp_packet->header().size(),
+                                  rtp_packet->packet_length(),
+                                  event.timestamp_us() / 1000, *parser_.get());
+      if (packet->valid_header()) {
+        // Check if the packet should not be filtered out.
+        if (!filter_.test(packet->header().payloadType) &&
+            !(use_ssrc_filter_ && packet->header().ssrc != ssrc_))
+          return packet;
+      } else {
+        std::cout << "Warning: Packet with index " << (rtp_packet_index_ - 1)
+                  << " has an invalid header and will be ignored." << std::endl;
+      }
+      // The packet has either an invalid header or needs to be filtered out, so
+      // it can be deleted.
+      delete packet;
+    }
+  }
+  return nullptr;
+}
+
+int64_t RtcEventLogSource::NextAudioOutputEventMs() {
+  while (audio_output_index_ < event_log_->stream_size()) {
+    const rtclog::Event& event = event_log_->stream(audio_output_index_);
+    const rtclog::DebugEvent* debug_event = GetAudioOutputEvent(event);
+    audio_output_index_++;
+    if (debug_event)
+      return event.timestamp_us() / 1000;
+  }
+  return std::numeric_limits<int64_t>::max();
+}
+
+RtcEventLogSource::RtcEventLogSource()
+    : PacketSource(), parser_(RtpHeaderParser::Create()) {}
+
+bool RtcEventLogSource::OpenFile(const std::string& file_name) {
+  event_log_.reset(new rtclog::EventStream());
+  return RtcEventLog::ParseRtcEventLog(file_name, event_log_.get());
+}
+
+}  // namespace test
+}  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h b/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h
new file mode 100644
index 0000000..d144b51
--- /dev/null
+++ b/webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTC_EVENT_LOG_SOURCE_H_
+#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTC_EVENT_LOG_SOURCE_H_
+
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
+
+namespace webrtc {
+
+class RtpHeaderParser;
+
+namespace rtclog {
+class EventStream;
+}  // namespace rtclog
+
+namespace test {
+
+class Packet;
+
+class RtcEventLogSource : public PacketSource {
+ public:
+  // Creates an RtcEventLogSource reading from |file_name|. If the file cannot
+  // be opened, or has the wrong format, NULL will be returned.
+  static RtcEventLogSource* Create(const std::string& file_name);
+
+  virtual ~RtcEventLogSource();
+
+  // Registers an RTP header extension and binds it to |id|.
+  virtual bool RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id);
+
+  // Returns a pointer to the next packet. Returns NULL if end of file was
+  // reached.
+  Packet* NextPacket() override;
+
+  // Returns the timestamp of the next audio output event, in milliseconds. The
+  // maximum value of int64_t is returned if there are no more audio output
+  // events available.
+  int64_t NextAudioOutputEventMs();
+
+ private:
+  RtcEventLogSource();
+
+  bool OpenFile(const std::string& file_name);
+
+  int rtp_packet_index_ = 0;
+  int audio_output_index_ = 0;
+
+  rtc::scoped_ptr<rtclog::EventStream> event_log_;
+  rtc::scoped_ptr<RtpHeaderParser> parser_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtcEventLogSource);
+};
+
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RTC_EVENT_LOG_SOURCE_H_