| /* |
| * 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 "api/task_queue/default_task_queue_factory.h" |
| #include "api/transport/field_trial_based_config.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/rtp_header_parser.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<VideoReceiveStream::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<VideoReceiveStream::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; |
| } |
| |
| // Setup the video streams based on the configuration. |
| webrtc::RtcEventLogNull event_log; |
| std::unique_ptr<TaskQueueFactory> task_queue_factory = |
| CreateDefaultTaskQueueFactory(); |
| Call::Config 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()); |
| |
| for (const auto& receive_stream : stream_state->receive_streams) { |
| call->DestroyVideoReceiveStream(receive_stream); |
| } |
| } |
| |
| std::vector<VideoReceiveStream::Config> RtpReplayer::ReadConfigFromFile( |
| const std::string& replay_config, |
| Transport* transport) { |
| Json::Reader json_reader; |
| Json::Value json_configs; |
| if (!json_reader.parse(replay_config, json_configs)) { |
| RTC_LOG(LS_ERROR) |
| << "Error parsing JSON replay configuration for the fuzzer" |
| << json_reader.getFormatedErrorMessages(); |
| return {}; |
| } |
| |
| std::vector<VideoReceiveStream::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<VideoReceiveStream::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); |
| decoder.decoder_factory = stream_state->decoder_factory.get(); |
| } |
| |
| // 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(); |
| 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) { |
| int64_t replay_start_ms = -1; |
| int num_packets = 0; |
| std::map<uint32_t, int> unknown_packets; |
| |
| 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)))); |
| } |
| |
| ++num_packets; |
| switch (call->Receiver()->DeliverPacket( |
| webrtc::MediaType::VIDEO, |
| rtc::CopyOnWriteBuffer(packet.data, packet.length), |
| /* packet_time_us */ -1)) { |
| case PacketReceiver::DELIVERY_OK: |
| break; |
| case PacketReceiver::DELIVERY_UNKNOWN_SSRC: { |
| RTPHeader header; |
| std::unique_ptr<RtpHeaderParser> parser( |
| RtpHeaderParser::CreateForTest()); |
| |
| parser->Parse(packet.data, packet.length, &header); |
| if (unknown_packets[header.ssrc] == 0) { |
| RTC_LOG(LS_ERROR) << "Unknown SSRC: " << header.ssrc; |
| } |
| ++unknown_packets[header.ssrc]; |
| break; |
| } |
| case PacketReceiver::DELIVERY_PACKET_ERROR: { |
| RTC_LOG(LS_ERROR) |
| << "Packet error, corrupt packets or incorrect setup?"; |
| RTPHeader header; |
| std::unique_ptr<RtpHeaderParser> parser( |
| RtpHeaderParser::CreateForTest()); |
| parser->Parse(packet.data, packet.length, &header); |
| RTC_LOG(LS_ERROR) << "Packet packet_length=" << packet.length |
| << " payload_type=" << header.payloadType |
| << " sequence_number=" << header.sequenceNumber |
| << " time_stamp=" << header.timestamp |
| << " ssrc=" << header.ssrc; |
| break; |
| } |
| } |
| } |
| RTC_LOG(LS_INFO) << "num_packets: " << num_packets; |
| |
| for (const auto& unknown_packet : unknown_packets) { |
| RTC_LOG(LS_ERROR) << "Packets for unknown ssrc " << unknown_packet.first |
| << ":" << unknown_packet.second; |
| } |
| } |
| |
| } // namespace test |
| } // namespace webrtc |