| /* |
| * 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 "test/fuzzers/utils/rtp_replayer.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/memory/memory.h" |
| #include "api/task_queue/default_task_queue_factory.h" |
| #include "api/transport/field_trial_based_config.h" |
| #include "api/units/timestamp.h" |
| #include "modules/rtp_rtcp/source/rtp_packet.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_received.h" |
| #include "rtc_base/strings/json.h" |
| #include "system_wrappers/include/clock.h" |
| #include "test/call_config_utils.h" |
| #include "test/encoder_settings.h" |
| #include "test/fake_decoder.h" |
| #include "test/rtp_file_reader.h" |
| #include "test/run_loop.h" |
| |
| namespace webrtc { |
| namespace test { |
| |
| void RtpReplayer::Replay(const std::string& replay_config_filepath, |
| const uint8_t* rtp_dump_data, |
| size_t rtp_dump_size) { |
| auto stream_state = std::make_unique<StreamState>(); |
| std::vector<VideoReceiveStreamInterface::Config> receive_stream_configs = |
| ReadConfigFromFile(replay_config_filepath, &(stream_state->transport)); |
| return Replay(std::move(stream_state), std::move(receive_stream_configs), |
| rtp_dump_data, rtp_dump_size); |
| } |
| |
| void RtpReplayer::Replay( |
| std::unique_ptr<StreamState> stream_state, |
| std::vector<VideoReceiveStreamInterface::Config> receive_stream_configs, |
| const uint8_t* rtp_dump_data, |
| size_t rtp_dump_size) { |
| RunLoop loop; |
| rtc::ScopedBaseFakeClock fake_clock; |
| |
| // Work around: webrtc calls webrtc::Random(clock.TimeInMicroseconds()) |
| // everywhere and Random expects non-zero seed. Let's set the clock non-zero |
| // to make them happy. |
| fake_clock.SetTime(webrtc::Timestamp::Millis(1)); |
| |
| // Attempt to create an RtpReader from the input file. |
| auto rtp_reader = CreateRtpReader(rtp_dump_data, rtp_dump_size); |
| if (rtp_reader == nullptr) { |
| RTC_LOG(LS_ERROR) << "Failed to create the rtp_reader"; |
| return; |
| } |
| |
| RtpHeaderExtensionMap extensions(/*extmap_allow_mixed=*/true); |
| // Skip i = 0 since it maps to kRtpExtensionNone. |
| for (int i = 1; i < kRtpExtensionNumberOfExtensions; i++) { |
| RTPExtensionType extension_type = static_cast<RTPExtensionType>(i); |
| // Extensions are registered with an ID, which you signal to the |
| // peer so they know what to expect. This code only cares about |
| // parsing so the value of the ID isn't relevant. |
| extensions.RegisterByType(i, extension_type); |
| } |
| |
| // Setup the video streams based on the configuration. |
| webrtc::RtcEventLogNull event_log; |
| std::unique_ptr<TaskQueueFactory> task_queue_factory = |
| CreateDefaultTaskQueueFactory(); |
| CallConfig call_config(&event_log); |
| call_config.task_queue_factory = task_queue_factory.get(); |
| FieldTrialBasedConfig field_trials; |
| call_config.trials = &field_trials; |
| std::unique_ptr<Call> call(Call::Create(call_config)); |
| SetupVideoStreams(&receive_stream_configs, stream_state.get(), call.get()); |
| |
| // Start replaying the provided stream now that it has been configured. |
| for (const auto& receive_stream : stream_state->receive_streams) { |
| receive_stream->Start(); |
| } |
| |
| ReplayPackets(&fake_clock, call.get(), rtp_reader.get(), extensions); |
| |
| for (const auto& receive_stream : stream_state->receive_streams) { |
| call->DestroyVideoReceiveStream(receive_stream); |
| } |
| } |
| |
| std::vector<VideoReceiveStreamInterface::Config> |
| RtpReplayer::ReadConfigFromFile(const std::string& replay_config, |
| Transport* transport) { |
| Json::CharReaderBuilder factory; |
| std::unique_ptr<Json::CharReader> json_reader = |
| absl::WrapUnique(factory.newCharReader()); |
| Json::Value json_configs; |
| Json::String errors; |
| if (!json_reader->parse(replay_config.data(), |
| replay_config.data() + replay_config.length(), |
| &json_configs, &errors)) { |
| RTC_LOG(LS_ERROR) |
| << "Error parsing JSON replay configuration for the fuzzer: " << errors; |
| return {}; |
| } |
| |
| std::vector<VideoReceiveStreamInterface::Config> receive_stream_configs; |
| receive_stream_configs.reserve(json_configs.size()); |
| for (const auto& json : json_configs) { |
| receive_stream_configs.push_back( |
| ParseVideoReceiveStreamJsonConfig(transport, json)); |
| } |
| return receive_stream_configs; |
| } |
| |
| void RtpReplayer::SetupVideoStreams( |
| std::vector<VideoReceiveStreamInterface::Config>* receive_stream_configs, |
| StreamState* stream_state, |
| Call* call) { |
| stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>(); |
| for (auto& receive_config : *receive_stream_configs) { |
| // Attach the decoder for the corresponding payload type in the config. |
| for (auto& decoder : receive_config.decoders) { |
| decoder = test::CreateMatchingDecoder(decoder.payload_type, |
| decoder.video_format.name); |
| } |
| |
| // Create the window to display the rendered video. |
| stream_state->sinks.emplace_back( |
| test::VideoRenderer::Create("Fuzzing WebRTC Video Config", 640, 480)); |
| // Create a receive stream for this config. |
| receive_config.renderer = stream_state->sinks.back().get(); |
| receive_config.decoder_factory = stream_state->decoder_factory.get(); |
| stream_state->receive_streams.emplace_back( |
| call->CreateVideoReceiveStream(std::move(receive_config))); |
| } |
| } |
| |
| std::unique_ptr<test::RtpFileReader> RtpReplayer::CreateRtpReader( |
| const uint8_t* rtp_dump_data, |
| size_t rtp_dump_size) { |
| std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create( |
| test::RtpFileReader::kRtpDump, rtp_dump_data, rtp_dump_size, {})); |
| if (!rtp_reader) { |
| RTC_LOG(LS_ERROR) << "Unable to open input file with any supported format"; |
| return nullptr; |
| } |
| return rtp_reader; |
| } |
| |
| void RtpReplayer::ReplayPackets( |
| rtc::FakeClock* clock, |
| Call* call, |
| test::RtpFileReader* rtp_reader, |
| const RtpPacketReceived::ExtensionManager& extensions) { |
| int64_t replay_start_ms = -1; |
| |
| while (true) { |
| int64_t now_ms = rtc::TimeMillis(); |
| if (replay_start_ms == -1) { |
| replay_start_ms = now_ms; |
| } |
| |
| test::RtpPacket packet; |
| if (!rtp_reader->NextPacket(&packet)) { |
| break; |
| } |
| |
| int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms; |
| if (deliver_in_ms > 0) { |
| // StatsCounter::ReportMetricToAggregatedCounter is O(elapsed time). |
| // Set an upper limit to prevent waste time. |
| clock->AdvanceTime(webrtc::TimeDelta::Millis( |
| std::min(deliver_in_ms, static_cast<int64_t>(100)))); |
| } |
| |
| RtpPacketReceived received_packet( |
| &extensions, Timestamp::Micros(clock->TimeNanos() / 1000)); |
| if (!received_packet.Parse(packet.data, packet.length)) { |
| RTC_LOG(LS_ERROR) << "Packet error, corrupt packets or incorrect setup?"; |
| break; |
| } |
| // Set the clock rate - always 90K for video |
| received_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency); |
| |
| call->Receiver()->DeliverRtpPacket( |
| MediaType::VIDEO, std::move(received_packet), |
| [&](const RtpPacketReceived& parsed_packet) { |
| RTC_LOG(LS_ERROR) << "Unknown SSRC: " << parsed_packet.Ssrc(); |
| return false; |
| }); |
| } |
| } |
| } // namespace test |
| } // namespace webrtc |