Add rtp_generator utility to rtc_tools.

This CL introduces a new rtp_generator tool that can be utilized to generate
.rtpdump files that can be replayed by the video_replayer. This allows
automated generation of corpus material for the new WebRTC RTP fuzzers in
addition to allowing anyone who is experimenting with a new RTP feature to
quickly debug issues.

It can be used as follows:
./rtp_generator --input_config=./rtc_tools/rtp_generator/configs/vp8.json --output_rtpdump=/tmp/vp8.rtpdump
./video_replay --config_file test/fuzzers/configs/replay_packet_fuzzer/vp8_config.json --input_file /tmp/vp8.rtpdump

It works by generating squares randomly on the screen for a given duration. This
initial version is very limited and doesn't support FEC, RED and other
configurations. I plan to extend it to support these in future CLs.

Bug: webrtc:10117
Change-Id: I31d3dbb6fad73c727145ead4e7d085113d11fc51
Reviewed-on: https://webrtc-review.googlesource.com/c/119964
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26517}
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index 8ebac7f..648f91d 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -137,6 +137,50 @@
   ]
 }
 
+rtc_executable("rtp_generator") {
+  visibility = [ "*" ]
+  testonly = true
+  sources = [
+    "rtp_generator/main.cc",
+    "rtp_generator/rtp_generator.cc",
+    "rtp_generator/rtp_generator.h",
+  ]
+
+  deps = [
+    ":command_line_parser",
+    "../api:libjingle_peerconnection_api",
+    "../api:transport_api",
+    "../api/video:builtin_video_bitrate_allocator_factory",
+    "../api/video_codecs:builtin_video_decoder_factory",
+    "../api/video_codecs:builtin_video_encoder_factory",
+    "../api/video_codecs:video_codecs_api",
+    "../call",
+    "../call:call_interfaces",
+    "../call:fake_network",
+    "../call:rtp_interfaces",
+    "../call:rtp_sender",
+    "../call:simulated_network",
+    "../call:simulated_packet_receiver",
+    "../call:video_stream_api",
+    "../logging:rtc_event_log_api",
+    "../logging:rtc_event_log_impl_base",
+    "../media:rtc_audio_video",
+    "../media:rtc_media_base",
+    "../rtc_base:rtc_base",
+    "../rtc_base:rtc_base_approved",
+    "../rtc_base:rtc_json",
+    "../rtc_base/system:file_wrapper",
+    "../test:fileutils",
+    "../test:rtp_test_utils",
+    "../test:video_test_common",
+    "//third_party/abseil-cpp/absl/strings",
+  ]
+  if (!build_with_chromium && is_clang) {
+    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+    suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+  }
+}
+
 if (!build_with_chromium && !build_with_mozilla) {
   action("frame_analyzer_host") {
     script = "//tools_webrtc/executable_host_build.py"
diff --git a/rtc_tools/DEPS b/rtc_tools/DEPS
index cfa9943..0cddb4a 100644
--- a/rtc_tools/DEPS
+++ b/rtc_tools/DEPS
@@ -3,6 +3,7 @@
   "+common_audio",
   "+common_video",
   "+logging/rtc_event_log",
+  "+media",
   "+modules/audio_device",
   "+modules/audio_coding/audio_network_adaptor",
   "+modules/audio_coding/neteq/include",
diff --git a/rtc_tools/rtp_generator/configs/vp8.json b/rtc_tools/rtp_generator/configs/vp8.json
new file mode 100644
index 0000000..65402fb
--- /dev/null
+++ b/rtc_tools/rtp_generator/configs/vp8.json
@@ -0,0 +1,13 @@
+{
+  "video_streams": [
+    {
+      "duration_ms": 10000,
+      "video_width": 640,
+      "video_height": 480,
+      "video_fps": 24,
+      "rtp" : {
+         "payload_name" : "VP8"
+      }
+    }
+  ]
+}
diff --git a/rtc_tools/rtp_generator/configs/vp9.json b/rtc_tools/rtp_generator/configs/vp9.json
new file mode 100644
index 0000000..fd780d8
--- /dev/null
+++ b/rtc_tools/rtp_generator/configs/vp9.json
@@ -0,0 +1,13 @@
+{
+  "video_streams": [
+    {
+      "duration_ms": 10000,
+      "video_width": 640,
+      "video_height": 480,
+      "video_fps": 24,
+      "rtp" : {
+         "payload_name" : "VP9"
+      }
+    }
+  ]
+}
diff --git a/rtc_tools/rtp_generator/main.cc b/rtc_tools/rtp_generator/main.cc
new file mode 100644
index 0000000..63958bc
--- /dev/null
+++ b/rtc_tools/rtp_generator/main.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2019 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 <stdlib.h>
+#include <string>
+
+#include "rtc_tools/rtp_generator/rtp_generator.h"
+#include "rtc_tools/simple_command_line_parser.h"
+
+int main(int argc, char* argv[]) {
+  const std::string usage =
+      "Generates custom configured rtpdumps for the purpose of testing.\n"
+      "Example Usage:\n"
+      "./rtp_generator --input_config=sender_config.json\n"
+      "                --output_rtpdump=my.rtpdump\n";
+
+  webrtc::test::CommandLineParser cmd_parser;
+  cmd_parser.Init(argc, argv);
+  cmd_parser.SetUsageMessage(usage);
+  cmd_parser.SetFlag("input_config", "");
+  cmd_parser.SetFlag("output_rtpdump", "");
+  cmd_parser.ProcessFlags();
+
+  const std::string config_path = cmd_parser.GetFlag("input_config");
+  const std::string rtp_dump_path = cmd_parser.GetFlag("output_rtpdump");
+
+  if (cmd_parser.GetFlag("help") == "true" || rtp_dump_path.empty() ||
+      config_path.empty()) {
+    cmd_parser.PrintUsageMessage();
+    return EXIT_FAILURE;
+  }
+
+  absl::optional<webrtc::RtpGeneratorOptions> options =
+      webrtc::ParseRtpGeneratorOptionsFromFile(config_path);
+  if (!options.has_value()) {
+    return EXIT_FAILURE;
+  }
+
+  webrtc::RtpGenerator rtp_generator(*options);
+  rtp_generator.GenerateRtpDump(rtp_dump_path);
+
+  return EXIT_SUCCESS;
+}
diff --git a/rtc_tools/rtp_generator/rtp_generator.cc b/rtc_tools/rtp_generator/rtp_generator.cc
new file mode 100644
index 0000000..3c9e22e
--- /dev/null
+++ b/rtc_tools/rtp_generator/rtp_generator.cc
@@ -0,0 +1,317 @@
+/*
+ *  Copyright (c) 2019 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 "rtc_tools/rtp_generator/rtp_generator.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "api/video_codecs/builtin_video_decoder_factory.h"
+#include "api/video_codecs/builtin_video_encoder_factory.h"
+#include "api/video_codecs/video_encoder.h"
+#include "api/video_codecs/video_encoder_config.h"
+#include "media/base/media_constants.h"
+#include "rtc_base/strings/json.h"
+#include "rtc_base/system/file_wrapper.h"
+#include "rtc_base/thread.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace {
+
+// Payload types.
+constexpr int kPayloadTypeVp8 = 125;
+constexpr int kPayloadTypeVp9 = 124;
+constexpr int kPayloadTypeH264 = 123;
+constexpr int kFakeVideoSendPayloadType = 122;
+
+// Defaults
+constexpr int kDefaultSsrc = 1337;
+constexpr int kMaxConfigBufferSize = 8192;
+
+// Utility function to validate a correct codec type has been passed in.
+bool IsValidCodecType(const std::string& codec_name) {
+  return cricket::kVp8CodecName == codec_name ||
+         cricket::kVp9CodecName == codec_name ||
+         cricket::kH264CodecName == codec_name;
+}
+
+// Utility function to return some base payload type for a codec_name.
+int GetDefaultTypeForPayloadName(const std::string& codec_name) {
+  if (cricket::kVp8CodecName == codec_name) {
+    return kPayloadTypeVp8;
+  }
+  if (cricket::kVp9CodecName == codec_name) {
+    return kPayloadTypeVp9;
+  }
+  if (cricket::kH264CodecName == codec_name) {
+    return kPayloadTypeH264;
+  }
+  return kFakeVideoSendPayloadType;
+}
+
+// Creates a single VideoSendStream configuration.
+absl::optional<RtpGeneratorOptions::VideoSendStreamConfig>
+ParseVideoSendStreamConfig(const Json::Value& json) {
+  RtpGeneratorOptions::VideoSendStreamConfig config;
+
+  // Parse video source settings.
+  if (!rtc::GetIntFromJsonObject(json, "duration_ms", &config.duration_ms)) {
+    RTC_LOG(LS_WARNING) << "duration_ms not specified using default: "
+                        << config.duration_ms;
+  }
+  if (!rtc::GetIntFromJsonObject(json, "video_width", &config.video_width)) {
+    RTC_LOG(LS_WARNING) << "video_width not specified using default: "
+                        << config.video_width;
+  }
+  if (!rtc::GetIntFromJsonObject(json, "video_height", &config.video_height)) {
+    RTC_LOG(LS_WARNING) << "video_height not specified using default: "
+                        << config.video_height;
+  }
+  if (!rtc::GetIntFromJsonObject(json, "video_fps", &config.video_fps)) {
+    RTC_LOG(LS_WARNING) << "video_fps not specified using default: "
+                        << config.video_fps;
+  }
+  if (!rtc::GetIntFromJsonObject(json, "num_squares", &config.num_squares)) {
+    RTC_LOG(LS_WARNING) << "num_squares not specified using default: "
+                        << config.num_squares;
+  }
+
+  // Parse RTP settings for this configuration.
+  config.rtp.ssrcs.push_back(kDefaultSsrc);
+  Json::Value rtp_json;
+  if (!rtc::GetValueFromJsonObject(json, "rtp", &rtp_json)) {
+    RTC_LOG(LS_ERROR) << "video_streams must have an rtp section";
+    return absl::nullopt;
+  }
+  if (!rtc::GetStringFromJsonObject(rtp_json, "payload_name",
+                                    &config.rtp.payload_name)) {
+    RTC_LOG(LS_ERROR) << "rtp.payload_name must be specified";
+    return absl::nullopt;
+  }
+  if (!IsValidCodecType(config.rtp.payload_name)) {
+    RTC_LOG(LS_ERROR) << "rtp.payload_name must be VP8,VP9 or H264";
+    return absl::nullopt;
+  }
+
+  config.rtp.payload_type =
+      GetDefaultTypeForPayloadName(config.rtp.payload_name);
+  if (!rtc::GetIntFromJsonObject(rtp_json, "payload_type",
+                                 &config.rtp.payload_type)) {
+    RTC_LOG(LS_WARNING)
+        << "rtp.payload_type not specified using default for codec type"
+        << config.rtp.payload_type;
+  }
+
+  return config;
+}
+
+}  // namespace
+
+absl::optional<RtpGeneratorOptions> ParseRtpGeneratorOptionsFromFile(
+    const std::string& options_file) {
+  if (!test::FileExists(options_file)) {
+    RTC_LOG(LS_ERROR) << " configuration file does not exist";
+    return absl::nullopt;
+  }
+
+  // Read the configuration file from disk.
+  FileWrapper config_file = FileWrapper::OpenReadOnly(options_file);
+  std::vector<char> raw_json_buffer(kMaxConfigBufferSize, 0);
+  size_t bytes_read =
+      config_file.Read(raw_json_buffer.data(), raw_json_buffer.size() - 1);
+  if (bytes_read == 0) {
+    RTC_LOG(LS_ERROR) << "Unable to read the configuration file.";
+    return absl::nullopt;
+  }
+
+  // Parse the file as JSON
+  Json::Reader json_reader;
+  Json::Value json;
+  if (!json_reader.parse(raw_json_buffer.data(), json)) {
+    RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file";
+    return absl::nullopt;
+  }
+
+  RtpGeneratorOptions gen_options;
+  for (const auto& video_stream_json : json["video_streams"]) {
+    absl::optional<RtpGeneratorOptions::VideoSendStreamConfig>
+        video_stream_config = ParseVideoSendStreamConfig(video_stream_json);
+    if (!video_stream_config.has_value()) {
+      RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file";
+      return absl::nullopt;
+    }
+    gen_options.video_streams.push_back(*video_stream_config);
+  }
+  return gen_options;
+}
+
+RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
+    : options_(options),
+      video_encoder_factory_(CreateBuiltinVideoEncoderFactory()),
+      video_decoder_factory_(CreateBuiltinVideoDecoderFactory()),
+      video_bitrate_allocator_factory_(
+          CreateBuiltinVideoBitrateAllocatorFactory()),
+      event_log_(webrtc::RtcEventLog::CreateNull()),
+      call_(Call::Create(CallConfig(event_log_.get()))) {
+  constexpr int kMinBitrateBps = 30000;    // 30 Kbps
+  constexpr int kMaxBitrateBps = 2500000;  // 2.5 Mbps
+
+  int stream_count = 0;
+  for (const auto& send_config : options.video_streams) {
+    webrtc::VideoSendStream::Config video_config(this);
+    video_config.encoder_settings.encoder_factory =
+        video_encoder_factory_.get();
+    video_config.encoder_settings.bitrate_allocator_factory =
+        video_bitrate_allocator_factory_.get();
+    video_config.rtp = send_config.rtp;
+    // Update some required to be unique values.
+    stream_count++;
+    video_config.rtp.mid = "mid-" + std::to_string(stream_count);
+    video_config.track_id = "track-" + std::to_string(stream_count);
+
+    // Configure the video encoder configuration.
+    VideoEncoderConfig encoder_config;
+    encoder_config.content_type =
+        VideoEncoderConfig::ContentType::kRealtimeVideo;
+    encoder_config.codec_type =
+        PayloadStringToCodecType(video_config.rtp.payload_name);
+    if (video_config.rtp.payload_name == cricket::kVp8CodecName) {
+      VideoCodecVP8 settings = VideoEncoder::GetDefaultVp8Settings();
+      encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
+          VideoEncoderConfig::Vp8EncoderSpecificSettings>(settings);
+    } else if (video_config.rtp.payload_name == cricket::kVp9CodecName) {
+      VideoCodecVP9 settings = VideoEncoder::GetDefaultVp9Settings();
+      encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
+          VideoEncoderConfig::Vp9EncoderSpecificSettings>(settings);
+    } else if (video_config.rtp.payload_name == cricket::kH264CodecName) {
+      VideoCodecH264 settings = VideoEncoder::GetDefaultH264Settings();
+      encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
+          VideoEncoderConfig::H264EncoderSpecificSettings>(settings);
+    }
+    encoder_config.video_format.name = video_config.rtp.payload_name;
+    encoder_config.min_transmit_bitrate_bps = 0;
+    encoder_config.max_bitrate_bps = kMaxBitrateBps;
+    encoder_config.content_type =
+        VideoEncoderConfig::ContentType::kRealtimeVideo;
+
+    // Configure the simulcast layers.
+    encoder_config.number_of_streams = video_config.rtp.ssrcs.size();
+    encoder_config.bitrate_priority = 1.0;
+    encoder_config.simulcast_layers.resize(encoder_config.number_of_streams);
+    for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
+      encoder_config.simulcast_layers[i].active = true;
+      encoder_config.simulcast_layers[i].min_bitrate_bps = kMinBitrateBps;
+      encoder_config.simulcast_layers[i].max_bitrate_bps = kMaxBitrateBps;
+      encoder_config.simulcast_layers[i].max_framerate = send_config.video_fps;
+    }
+
+    encoder_config.video_stream_factory =
+        new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+            video_config.rtp.payload_name, /*max qp*/ 56, /*screencast*/ false,
+            /*screenshare enabled*/ false);
+
+    // Setup the fake video stream for this.
+    std::unique_ptr<test::FrameGeneratorCapturer> frame_generator(
+        test::FrameGeneratorCapturer::Create(
+            send_config.video_width, send_config.video_height, absl::nullopt,
+            absl::nullopt, send_config.video_fps, Clock::GetRealTimeClock()));
+    frame_generator->Init();
+
+    VideoSendStream* video_send_stream = call_->CreateVideoSendStream(
+        std::move(video_config), std::move(encoder_config));
+    video_send_stream->SetSource(
+        frame_generator.get(),
+        webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
+    // Store these objects so we can destropy them at the end.
+    frame_generators_.push_back(std::move(frame_generator));
+    video_send_streams_.push_back(video_send_stream);
+  }
+}
+
+RtpGenerator::~RtpGenerator() {
+  for (VideoSendStream* send_stream : video_send_streams_) {
+    call_->DestroyVideoSendStream(send_stream);
+  }
+}
+
+void RtpGenerator::GenerateRtpDump(const std::string& rtp_dump_path) {
+  rtp_dump_writer_.reset(test::RtpFileWriter::Create(
+      test::RtpFileWriter::kRtpDump, rtp_dump_path));
+
+  call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
+                                   webrtc::kNetworkUp);
+  for (VideoSendStream* send_stream : video_send_streams_) {
+    send_stream->Start();
+  }
+
+  // Spinlock until all the durations end.
+  WaitUntilAllVideoStreamsFinish();
+
+  call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
+                                   webrtc::kNetworkDown);
+}
+
+bool RtpGenerator::SendRtp(const uint8_t* packet,
+                           size_t length,
+                           const webrtc::PacketOptions& options) {
+  test::RtpPacket rtp_packet = DataToRtpPacket(packet, length);
+  rtp_dump_writer_->WritePacket(&rtp_packet);
+  return true;
+}
+
+bool RtpGenerator::SendRtcp(const uint8_t* packet, size_t length) {
+  test::RtpPacket rtcp_packet = DataToRtpPacket(packet, length);
+  rtp_dump_writer_->WritePacket(&rtcp_packet);
+  return true;
+}
+
+int RtpGenerator::GetMaxDuration() const {
+  int max_end_ms = 0;
+  for (const auto& video_stream : options_.video_streams) {
+    max_end_ms = std::max(video_stream.duration_ms, max_end_ms);
+  }
+  return max_end_ms;
+}
+
+void RtpGenerator::WaitUntilAllVideoStreamsFinish() {
+  // Find the maximum duration required by the streams.
+  start_ms_ = Clock::GetRealTimeClock()->TimeInMilliseconds();
+  int64_t max_end_ms = start_ms_ + GetMaxDuration();
+
+  int64_t current_time = 0;
+  do {
+    int64_t min_wait_time = 0;
+    current_time = Clock::GetRealTimeClock()->TimeInMilliseconds();
+    // Stop any streams that are no longer active.
+    for (size_t i = 0; i < options_.video_streams.size(); ++i) {
+      const int64_t end_ms = start_ms_ + options_.video_streams[i].duration_ms;
+      if (current_time > end_ms) {
+        video_send_streams_[i]->Stop();
+      } else {
+        min_wait_time = std::min(min_wait_time, end_ms - current_time);
+      }
+    }
+    rtc::Thread::Current()->SleepMs(min_wait_time);
+  } while (current_time < max_end_ms);
+}
+
+test::RtpPacket RtpGenerator::DataToRtpPacket(const uint8_t* packet,
+                                              size_t packet_len) {
+  webrtc::test::RtpPacket rtp_packet;
+  memcpy(rtp_packet.data, packet, packet_len);
+  rtp_packet.length = packet_len;
+  rtp_packet.original_length = packet_len;
+  rtp_packet.time_ms =
+      webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_;
+  return rtp_packet;
+}
+
+}  // namespace webrtc
diff --git a/rtc_tools/rtp_generator/rtp_generator.h b/rtc_tools/rtp_generator/rtp_generator.h
new file mode 100644
index 0000000..34bc2e9
--- /dev/null
+++ b/rtc_tools/rtp_generator/rtp_generator.h
@@ -0,0 +1,122 @@
+/*
+ *  Copyright (c) 2019 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 RTC_TOOLS_RTP_GENERATOR_RTP_GENERATOR_H_
+#define RTC_TOOLS_RTP_GENERATOR_RTP_GENERATOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/call/transport.h"
+#include "api/media_types.h"
+#include "api/video/builtin_video_bitrate_allocator_factory.h"
+#include "api/video_codecs/video_decoder_factory.h"
+#include "api/video_codecs/video_encoder_config.h"
+#include "api/video_codecs/video_encoder_factory.h"
+#include "call/call.h"
+#include "call/rtp_config.h"
+#include "call/video_send_stream.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
+#include "media/engine/webrtc_video_engine.h"
+#include "rtc_base/constructor_magic.h"
+#include "test/frame_generator.h"
+#include "test/frame_generator_capturer.h"
+#include "test/rtp_file_reader.h"
+#include "test/rtp_file_writer.h"
+
+namespace webrtc {
+
+// Specifies all the configurable options to pass to the corpus generator.
+// If modified please update the JSON parser as well as all.
+struct RtpGeneratorOptions {
+  struct VideoSendStreamConfig {
+    // The time to record the RtpDump for.
+    int duration_ms = 10000;
+    // The video resolution width.
+    int video_width = 640;
+    // The video resolution height.
+    int video_height = 480;
+    // The video fps.
+    int video_fps = 24;
+    // The number of squares to render.
+    int num_squares = 128;
+    // The individual RTP configuration.
+    RtpConfig rtp;
+  };
+  // Multiple senders can be active at once on an rtp channel.
+  std::vector<VideoSendStreamConfig> video_streams;
+};
+
+// Attempts to parse RtpGeneratorOptions from a JSON file. Any failures
+// will result in absl::nullopt.
+absl::optional<RtpGeneratorOptions> ParseRtpGeneratorOptionsFromFile(
+    const std::string& options_file);
+
+// The RtpGenerator allows generating of corpus material intended to be
+// used by fuzzers. It accepts a simple Json configuration file that allows the
+// user to configure the codec, extensions and error correction mechanisms. It
+// will then proceed to generate an rtpdump for the specified duration using
+// that configuration that can be replayed by the video_replayer. The receiver
+// configuration JSON will also be output and can be replayed as follows:
+// ./rtp_generator --config_file sender_config --output_rtpdump my.rtpdump
+//                 --output_config receiver_config.json
+// ./video_replay --config_file receiver_config.json --output_file my.rtpdump
+//
+// It achieves this by creating a VideoStreamSender, configuring it as requested
+// by the user and then intercepting all outgoing RTP packets and writing them
+// to a file instead of out of the network. It then uses this sender
+// configuration to generate a mirror receiver configuration that can be read by
+// the video_replay program.
+class RtpGenerator final : public webrtc::Transport {
+ public:
+  // Construct a new RtpGenerator using the specified options.
+  explicit RtpGenerator(const RtpGeneratorOptions& options);
+  // Cleans up the VideoSendStream.
+  ~RtpGenerator() override;
+  // Generates an rtp_dump that is written out to
+  void GenerateRtpDump(const std::string& rtp_dump_path);
+
+ private:
+  // webrtc::Transport implementation
+  // Captured RTP packets are written to the RTPDump file instead of over the
+  // network.
+  bool SendRtp(const uint8_t* packet,
+               size_t length,
+               const webrtc::PacketOptions& options) override;
+  // RTCP packets are ignored for now.
+  bool SendRtcp(const uint8_t* packet, size_t length) override;
+  // Returns the maximum duration
+  int GetMaxDuration() const;
+  // Waits until all video streams have finished.
+  void WaitUntilAllVideoStreamsFinish();
+  // Converts packet data into an RtpPacket.
+  test::RtpPacket DataToRtpPacket(const uint8_t* packet, size_t packet_len);
+
+  const RtpGeneratorOptions options_;
+  std::unique_ptr<VideoEncoderFactory> video_encoder_factory_;
+  std::unique_ptr<VideoDecoderFactory> video_decoder_factory_;
+  std::unique_ptr<VideoBitrateAllocatorFactory>
+      video_bitrate_allocator_factory_;
+  std::unique_ptr<RtcEventLog> event_log_;
+  std::unique_ptr<Call> call_;
+  std::unique_ptr<test::RtpFileWriter> rtp_dump_writer_;
+  std::vector<std::unique_ptr<test::FrameGeneratorCapturer>> frame_generators_;
+  std::vector<VideoSendStream*> video_send_streams_;
+  std::vector<uint32_t> durations_ms_;
+  uint32_t start_ms_ = 0;
+
+  // This object cannot be copied.
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpGenerator);
+};
+
+}  // namespace webrtc
+
+#endif  // RTC_TOOLS_RTP_GENERATOR_RTP_GENERATOR_H_
diff --git a/test/fuzzers/configs/replay_packet_fuzzer/vp8_config.json b/test/fuzzers/configs/replay_packet_fuzzer/vp8_config.json
index 1fb5618..2330659 100644
--- a/test/fuzzers/configs/replay_packet_fuzzer/vp8_config.json
+++ b/test/fuzzers/configs/replay_packet_fuzzer/vp8_config.json
@@ -3,65 +3,24 @@
          {
             "codec_params" : [],
             "payload_name" : "VP8",
-            "payload_type" : 96
-         },
-         {
-            "codec_params" : [],
-            "payload_name" : "VP9",
-            "payload_type" : 98
+            "payload_type" : 125
          }
       ],
       "render_delay_ms" : 10,
       "rtp" : {
-         "extensions" : [
-            {
-               "encrypt" : false,
-               "id" : 5,
-               "uri" : "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
-            },
-            {
-               "encrypt" : false,
-               "id" : 3,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
-            },
-            {
-               "encrypt" : false,
-               "id" : 6,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"
-            },
-            {
-               "encrypt" : false,
-               "id" : 4,
-               "uri" : "urn:3gpp:video-orientation"
-            },
-            {
-               "encrypt" : false,
-               "id" : 2,
-               "uri" : "urn:ietf:params:rtp-hdrext:toffset"
-            }
-         ],
-         "local_ssrc" : 2129427193,
+         "extensions" : [],
+         "local_ssrc" : 7331,
          "nack" : {
             "rtp_history_ms" : 1000
          },
-         "red_payload_type" : 100,
+         "red_payload_type" : -1,
          "remb" : true,
-         "remote_ssrc" : 2060300048,
+         "remote_ssrc" : 1337,
          "rtcp_mode" : "RtcpMode::kCompound",
-         "rtx_payload_types" : [
-            {
-               "97" : 96
-            },
-            {
-               "99" : 98
-            },
-            {
-               "101" : 100
-            }
-         ],
+         "rtx_payload_types" : [],
          "rtx_ssrc" : 100,
          "transport_cc" : true,
-         "ulpfec_payload_type" : 102
+         "ulpfec_payload_type" : -1
       },
       "target_delay_ms" : 0
 }]
diff --git a/test/fuzzers/configs/replay_packet_fuzzer/vp9_config.json b/test/fuzzers/configs/replay_packet_fuzzer/vp9_config.json
index 7010a27..cda637f 100644
--- a/test/fuzzers/configs/replay_packet_fuzzer/vp9_config.json
+++ b/test/fuzzers/configs/replay_packet_fuzzer/vp9_config.json
@@ -1,71 +1,26 @@
-[
-   {
+[{
       "decoders" : [
          {
             "codec_params" : [],
             "payload_name" : "VP9",
-            "payload_type" : 96
+            "payload_type" : 124
          }
       ],
       "render_delay_ms" : 10,
       "rtp" : {
-         "extensions" : [
-            {
-               "encrypt" : false,
-               "id" : 5,
-               "uri" : "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
-            },
-            {
-               "encrypt" : false,
-               "id" : 3,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
-            },
-            {
-               "encrypt" : false,
-               "id" : 6,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"
-            },
-            {
-               "encrypt" : false,
-               "id" : 7,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"
-            },
-            {
-               "encrypt" : false,
-               "id" : 8,
-               "uri" : "http://www.webrtc.org/experiments/rtp-hdrext/video-timing"
-            },
-            {
-               "encrypt" : false,
-               "id" : 4,
-               "uri" : "urn:3gpp:video-orientation"
-            },
-            {
-               "encrypt" : false,
-               "id" : 2,
-               "uri" : "urn:ietf:params:rtp-hdrext:toffset"
-            }
-         ],
-         "local_ssrc" : 1,
+         "extensions" : [],
+         "local_ssrc" : 7331,
          "nack" : {
             "rtp_history_ms" : 1000
          },
-         "red_payload_type" : 100,
+         "red_payload_type" : -1,
          "remb" : true,
-         "remote_ssrc" : 359403851,
-         "rtcp_mode" : "RtcpMode::kReducedSize",
-         "rtx_payload_types" : [
-            {
-               "97" : 96
-            },
-            {
-               "101" : 100
-            }
-         ],
-         "rtx_ssrc" : 1732909516,
+         "remote_ssrc" : 1337,
+         "rtcp_mode" : "RtcpMode::kCompound",
+         "rtx_payload_types" : [],
+         "rtx_ssrc" : 100,
          "transport_cc" : true,
-         "ulpfec_payload_type" : 127
+         "ulpfec_payload_type" : -1
       },
       "target_delay_ms" : 0
-   }
-]
+}]