diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_tools/rtp_generator')
5 files changed, 528 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_tools/rtp_generator/configs/vp8.json b/third_party/libwebrtc/rtc_tools/rtp_generator/configs/vp8.json new file mode 100644 index 0000000000..65402fb846 --- /dev/null +++ b/third_party/libwebrtc/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/third_party/libwebrtc/rtc_tools/rtp_generator/configs/vp9.json b/third_party/libwebrtc/rtc_tools/rtp_generator/configs/vp9.json new file mode 100644 index 0000000000..fd780d8151 --- /dev/null +++ b/third_party/libwebrtc/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/third_party/libwebrtc/rtc_tools/rtp_generator/main.cc b/third_party/libwebrtc/rtc_tools/rtp_generator/main.cc new file mode 100644 index 0000000000..df49576f39 --- /dev/null +++ b/third_party/libwebrtc/rtc_tools/rtp_generator/main.cc @@ -0,0 +1,48 @@ +/* + * 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 "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "rtc_tools/rtp_generator/rtp_generator.h" + +ABSL_FLAG(std::string, input_config, "", "JSON file with config"); +ABSL_FLAG(std::string, output_rtpdump, "", "Where to store the rtpdump"); + +int main(int argc, char* argv[]) { + absl::SetProgramUsageMessage( + "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"); + absl::ParseCommandLine(argc, argv); + + const std::string config_path = absl::GetFlag(FLAGS_input_config); + const std::string rtp_dump_path = absl::GetFlag(FLAGS_output_rtpdump); + + if (rtp_dump_path.empty() || config_path.empty()) { + 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/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc new file mode 100644 index 0000000000..e1a2cb30da --- /dev/null +++ b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.cc @@ -0,0 +1,330 @@ +/* + * 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 <memory> +#include <utility> + +#include "api/task_queue/default_task_queue_factory.h" +#include "api/test/create_frame_generator.h" +#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 "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" +#include "video/config/encoder_stream_factory.h" +#include "video/config/video_encoder_config.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::CharReaderBuilder builder; + Json::Value json; + std::string error_message; + std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader()); + if (!json_reader->parse(raw_json_buffer.data(), + raw_json_buffer.data() + raw_json_buffer.size(), + &json, &error_message)) { + RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file. Error:" + << error_message; + 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_(std::make_unique<RtcEventLogNull>()), + call_(Call::Create(CallConfig(event_log_.get()))), + task_queue_(CreateDefaultTaskQueueFactory()) { + constexpr int kMinBitrateBps = 30000; // 30 Kbps + constexpr int kMaxBitrateBps = 2500000; // 2.5 Mbps + + int stream_count = 0; + webrtc::VideoEncoder::EncoderInfo encoder_info; + 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); + + // 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 = + rtc::make_ref_counted<VideoEncoderConfig::Vp8EncoderSpecificSettings>( + settings); + } else if (video_config.rtp.payload_name == cricket::kVp9CodecName) { + VideoCodecVP9 settings = VideoEncoder::GetDefaultVp9Settings(); + encoder_config.encoder_specific_settings = + rtc::make_ref_counted<VideoEncoderConfig::Vp9EncoderSpecificSettings>( + settings); + } else if (video_config.rtp.payload_name == cricket::kH264CodecName) { + encoder_config.encoder_specific_settings = nullptr; + } + 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 = + rtc::make_ref_counted<cricket::EncoderStreamFactory>( + video_config.rtp.payload_name, /*max qp*/ 56, /*screencast*/ false, + /*screenshare enabled*/ false, encoder_info); + + // Setup the fake video stream for this. + std::unique_ptr<test::FrameGeneratorCapturer> frame_generator = + std::make_unique<test::FrameGeneratorCapturer>( + Clock::GetRealTimeClock(), + test::CreateSquareFrameGenerator(send_config.video_width, + send_config.video_height, + absl::nullopt, absl::nullopt), + send_config.video_fps, *task_queue_); + 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/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h new file mode 100644 index 0000000000..9a56522c33 --- /dev/null +++ b/third_party/libwebrtc/rtc_tools/rtp_generator/rtp_generator.h @@ -0,0 +1,124 @@ +/* + * 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/rtc_event_log/rtc_event_log.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "api/video_codecs/video_decoder_factory.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 "media/engine/webrtc_video_engine.h" +#include "test/frame_generator_capturer.h" +#include "test/rtp_file_reader.h" +#include "test/rtp_file_writer.h" +#include "video/config/video_encoder_config.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); + + RtpGenerator() = delete; + RtpGenerator(const RtpGenerator&) = delete; + RtpGenerator& operator=(const RtpGenerator&) = delete; + + // 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; + std::unique_ptr<TaskQueueFactory> task_queue_; +}; + +} // namespace webrtc + +#endif // RTC_TOOLS_RTP_GENERATOR_RTP_GENERATOR_H_ |