/* * 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 #include #include #include #include #include #include #include "call/call.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" #include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" #include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" #include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" #include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" #include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" #include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" #include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" #include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" #include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" #include "logging/rtc_event_log/output/rtc_event_log_output_file.h" #include "logging/rtc_event_log/rtc_event_log.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "logging/rtc_event_log/rtc_event_log_unittest_helper.h" #include "logging/rtc_event_log/rtc_stream_config.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "modules/remote_bitrate_estimator/include/bwe_defines.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "rtc_base/fakeclock.h" #include "rtc_base/ptr_util.h" #include "rtc_base/random.h" #include "test/gtest.h" #include "test/testsupport/fileutils.h" namespace webrtc { namespace { const uint8_t kTransmissionTimeOffsetExtensionId = 1; const uint8_t kAbsoluteSendTimeExtensionId = 14; const uint8_t kTransportSequenceNumberExtensionId = 13; const uint8_t kAudioLevelExtensionId = 9; const uint8_t kVideoRotationExtensionId = 5; const uint8_t kExtensionIds[] = { kTransmissionTimeOffsetExtensionId, kAbsoluteSendTimeExtensionId, kTransportSequenceNumberExtensionId, kAudioLevelExtensionId, kVideoRotationExtensionId}; const RTPExtensionType kExtensionTypes[] = { RTPExtensionType::kRtpExtensionTransmissionTimeOffset, RTPExtensionType::kRtpExtensionAbsoluteSendTime, RTPExtensionType::kRtpExtensionTransportSequenceNumber, RTPExtensionType::kRtpExtensionAudioLevel, RTPExtensionType::kRtpExtensionVideoRotation}; const char* kExtensionNames[] = { RtpExtension::kTimestampOffsetUri, RtpExtension::kAbsSendTimeUri, RtpExtension::kTransportSequenceNumberUri, RtpExtension::kAudioLevelUri, RtpExtension::kVideoRotationUri}; const size_t kNumExtensions = 5; struct BweLossEvent { int32_t bitrate_bps; uint8_t fraction_loss; int32_t total_packets; }; // TODO(terelius): Merge with event type in parser once updated? enum class EventType { kIncomingRtp, kOutgoingRtp, kIncomingRtcp, kOutgoingRtcp, kAudioPlayout, kBweLossUpdate, kBweDelayUpdate, kVideoRecvConfig, kVideoSendConfig, kAudioRecvConfig, kAudioSendConfig, kAudioNetworkAdaptation, kBweProbeClusterCreated, kBweProbeResult, }; const std::map event_type_to_string( {{EventType::kIncomingRtp, "RTP(in)"}, {EventType::kOutgoingRtp, "RTP(out)"}, {EventType::kIncomingRtcp, "RTCP(in)"}, {EventType::kOutgoingRtcp, "RTCP(out)"}, {EventType::kAudioPlayout, "PLAYOUT"}, {EventType::kBweLossUpdate, "BWE_LOSS"}, {EventType::kBweDelayUpdate, "BWE_DELAY"}, {EventType::kVideoRecvConfig, "VIDEO_RECV_CONFIG"}, {EventType::kVideoSendConfig, "VIDEO_SEND_CONFIG"}, {EventType::kAudioRecvConfig, "AUDIO_RECV_CONFIG"}, {EventType::kAudioSendConfig, "AUDIO_SEND_CONFIG"}, {EventType::kAudioNetworkAdaptation, "AUDIO_NETWORK_ADAPTATION"}, {EventType::kBweProbeClusterCreated, "BWE_PROBE_CREATED"}, {EventType::kBweProbeResult, "BWE_PROBE_RESULT"}}); const std::map parsed_event_type_to_string( {{ParsedRtcEventLog::EventType::UNKNOWN_EVENT, "UNKNOWN_EVENT"}, {ParsedRtcEventLog::EventType::LOG_START, "LOG_START"}, {ParsedRtcEventLog::EventType::LOG_END, "LOG_END"}, {ParsedRtcEventLog::EventType::RTP_EVENT, "RTP"}, {ParsedRtcEventLog::EventType::RTCP_EVENT, "RTCP"}, {ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT, "AUDIO_PLAYOUT"}, {ParsedRtcEventLog::EventType::LOSS_BASED_BWE_UPDATE, "LOSS_BASED_BWE_UPDATE"}, {ParsedRtcEventLog::EventType::DELAY_BASED_BWE_UPDATE, "DELAY_BASED_BWE_UPDATE"}, {ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT, "VIDEO_RECV_CONFIG"}, {ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT, "VIDEO_SEND_CONFIG"}, {ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT, "AUDIO_RECV_CONFIG"}, {ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT, "AUDIO_SEND_CONFIG"}, {ParsedRtcEventLog::EventType::AUDIO_NETWORK_ADAPTATION_EVENT, "AUDIO_NETWORK_ADAPTATION"}, {ParsedRtcEventLog::EventType::BWE_PROBE_CLUSTER_CREATED_EVENT, "BWE_PROBE_CREATED"}, {ParsedRtcEventLog::EventType::BWE_PROBE_RESULT_EVENT, "BWE_PROBE_RESULT"}}); } // namespace void PrintActualEvents(const ParsedRtcEventLog& parsed_log, std::ostream& stream); RtpPacketToSend GenerateOutgoingRtpPacket( const RtpHeaderExtensionMap* extensions, uint32_t csrcs_count, size_t packet_size, Random* prng) { RTC_CHECK_GE(packet_size, 16 + 4 * csrcs_count + 4 * kNumExtensions); std::vector csrcs; for (unsigned i = 0; i < csrcs_count; i++) { csrcs.push_back(prng->Rand()); } RtpPacketToSend rtp_packet(extensions, packet_size); rtp_packet.SetPayloadType(prng->Rand(127)); rtp_packet.SetMarker(prng->Rand()); rtp_packet.SetSequenceNumber(prng->Rand()); rtp_packet.SetSsrc(prng->Rand()); rtp_packet.SetTimestamp(prng->Rand()); rtp_packet.SetCsrcs(csrcs); rtp_packet.SetExtension(prng->Rand(0x00ffffff)); rtp_packet.SetExtension(prng->Rand(), prng->Rand(127)); rtp_packet.SetExtension(prng->Rand(0x00ffffff)); rtp_packet.SetExtension(prng->Rand(2)); rtp_packet.SetExtension(prng->Rand()); size_t payload_size = packet_size - rtp_packet.headers_size(); uint8_t* payload = rtp_packet.AllocatePayload(payload_size); for (size_t i = 0; i < payload_size; i++) { payload[i] = prng->Rand(); } return rtp_packet; } RtpPacketReceived GenerateIncomingRtpPacket( const RtpHeaderExtensionMap* extensions, uint32_t csrcs_count, size_t packet_size, Random* prng) { RtpPacketToSend packet_out = GenerateOutgoingRtpPacket(extensions, csrcs_count, packet_size, prng); RtpPacketReceived packet_in(extensions); packet_in.Parse(packet_out.data(), packet_out.size()); return packet_in; } rtc::Buffer GenerateRtcpPacket(Random* prng) { rtcp::ReportBlock report_block; report_block.SetMediaSsrc(prng->Rand()); // Remote SSRC. report_block.SetFractionLost(prng->Rand(50)); rtcp::SenderReport sender_report; sender_report.SetSenderSsrc(prng->Rand()); sender_report.SetNtp(NtpTime(prng->Rand(), prng->Rand())); sender_report.SetPacketCount(prng->Rand()); sender_report.AddReportBlock(report_block); return sender_report.Build(); } void GenerateVideoReceiveConfig(const RtpHeaderExtensionMap& extensions, rtclog::StreamConfig* config, Random* prng) { // Add SSRCs for the stream. config->remote_ssrc = prng->Rand(); config->local_ssrc = prng->Rand(); // Add extensions and settings for RTCP. config->rtcp_mode = prng->Rand() ? RtcpMode::kCompound : RtcpMode::kReducedSize; config->remb = prng->Rand(); config->rtx_ssrc = prng->Rand(); config->codecs.emplace_back(prng->Rand() ? "VP8" : "H264", prng->Rand(1, 127), prng->Rand(1, 127)); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { uint8_t id = extensions.GetId(kExtensionTypes[i]); if (id != RtpHeaderExtensionMap::kInvalidId) { config->rtp_extensions.emplace_back(kExtensionNames[i], id); } } } void GenerateVideoSendConfig(const RtpHeaderExtensionMap& extensions, rtclog::StreamConfig* config, Random* prng) { config->codecs.emplace_back(prng->Rand() ? "VP8" : "H264", prng->Rand(1, 127), prng->Rand(1, 127)); config->local_ssrc = prng->Rand(); config->rtx_ssrc = prng->Rand(); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { uint8_t id = extensions.GetId(kExtensionTypes[i]); if (id != RtpHeaderExtensionMap::kInvalidId) { config->rtp_extensions.emplace_back(kExtensionNames[i], id); } } } void GenerateAudioReceiveConfig(const RtpHeaderExtensionMap& extensions, rtclog::StreamConfig* config, Random* prng) { // Add SSRCs for the stream. config->remote_ssrc = prng->Rand(); config->local_ssrc = prng->Rand(); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { uint8_t id = extensions.GetId(kExtensionTypes[i]); if (id != RtpHeaderExtensionMap::kInvalidId) { config->rtp_extensions.emplace_back(kExtensionNames[i], id); } } } void GenerateAudioSendConfig(const RtpHeaderExtensionMap& extensions, rtclog::StreamConfig* config, Random* prng) { // Add SSRC to the stream. config->local_ssrc = prng->Rand(); // Add header extensions. for (unsigned i = 0; i < kNumExtensions; i++) { uint8_t id = extensions.GetId(kExtensionTypes[i]); if (id != RtpHeaderExtensionMap::kInvalidId) { config->rtp_extensions.emplace_back(kExtensionNames[i], id); } } } BweLossEvent GenerateBweLossEvent(Random* prng) { BweLossEvent loss_event; loss_event.bitrate_bps = prng->Rand(6000, 10000000); loss_event.fraction_loss = prng->Rand(); loss_event.total_packets = prng->Rand(1, 1000); return loss_event; } void GenerateAudioNetworkAdaptation(const RtpHeaderExtensionMap& extensions, AudioEncoderRuntimeConfig* config, Random* prng) { config->bitrate_bps = prng->Rand(0, 3000000); config->enable_fec = prng->Rand(); config->enable_dtx = prng->Rand(); config->frame_length_ms = prng->Rand(10, 120); config->num_channels = prng->Rand(1, 2); config->uplink_packet_loss_fraction = prng->Rand(); } class RtcEventLogSession : public ::testing::TestWithParam> { public: RtcEventLogSession() : prng(std::get<0>(GetParam())), output_period_ms(std::get<1>(GetParam())) {} void GenerateSessionDescription(size_t incoming_rtp_count, size_t outgoing_rtp_count, size_t incoming_rtcp_count, size_t outgoing_rtcp_count, size_t playout_count, size_t bwe_loss_count, size_t bwe_delay_count, const RtpHeaderExtensionMap& extensions, uint32_t csrcs_count); void WriteSession(); void ReadAndVerifySession(); void PrintExpectedEvents(std::ostream& stream); private: std::vector incoming_rtp_packets; std::vector outgoing_rtp_packets; std::vector incoming_rtcp_packets; std::vector outgoing_rtcp_packets; std::vector playout_ssrcs; std::vector bwe_loss_updates; std::vector > bwe_delay_updates; std::vector receiver_configs; std::vector sender_configs; std::vector event_types; Random prng; int64_t output_period_ms; }; void RtcEventLogSession::GenerateSessionDescription( size_t incoming_rtp_count, size_t outgoing_rtp_count, size_t incoming_rtcp_count, size_t outgoing_rtcp_count, size_t playout_count, size_t bwe_loss_count, size_t bwe_delay_count, const RtpHeaderExtensionMap& extensions, uint32_t csrcs_count) { // Create configuration for the video receive stream. receiver_configs.push_back(rtclog::StreamConfig()); GenerateVideoReceiveConfig(extensions, &receiver_configs.back(), &prng); event_types.push_back(EventType::kVideoRecvConfig); // Create configuration for the video send stream. sender_configs.push_back(rtclog::StreamConfig()); GenerateVideoSendConfig(extensions, &sender_configs.back(), &prng); event_types.push_back(EventType::kVideoSendConfig); const size_t config_count = 2; // Create incoming and outgoing RTP packets containing random data. for (size_t i = 0; i < incoming_rtp_count; i++) { size_t packet_size = prng.Rand(1000, 1100); incoming_rtp_packets.push_back(GenerateIncomingRtpPacket( &extensions, csrcs_count, packet_size, &prng)); event_types.push_back(EventType::kIncomingRtp); } for (size_t i = 0; i < outgoing_rtp_count; i++) { size_t packet_size = prng.Rand(1000, 1100); outgoing_rtp_packets.push_back(GenerateOutgoingRtpPacket( &extensions, csrcs_count, packet_size, &prng)); event_types.push_back(EventType::kOutgoingRtp); } // Create incoming and outgoing RTCP packets containing random data. for (size_t i = 0; i < incoming_rtcp_count; i++) { incoming_rtcp_packets.push_back(GenerateRtcpPacket(&prng)); event_types.push_back(EventType::kIncomingRtcp); } for (size_t i = 0; i < outgoing_rtcp_count; i++) { outgoing_rtcp_packets.push_back(GenerateRtcpPacket(&prng)); event_types.push_back(EventType::kOutgoingRtcp); } // Create random SSRCs to use when logging AudioPlayout events. for (size_t i = 0; i < playout_count; i++) { playout_ssrcs.push_back(prng.Rand()); event_types.push_back(EventType::kAudioPlayout); } // Create random bitrate updates for LossBasedBwe. for (size_t i = 0; i < bwe_loss_count; i++) { bwe_loss_updates.push_back(GenerateBweLossEvent(&prng)); event_types.push_back(EventType::kBweLossUpdate); } // Create random bitrate updates for DelayBasedBwe. for (size_t i = 0; i < bwe_delay_count; i++) { bwe_delay_updates.push_back(std::make_pair( prng.Rand(6000, 10000000), prng.Rand() ? BandwidthUsage::kBwOverusing : BandwidthUsage::kBwUnderusing)); event_types.push_back(EventType::kBweDelayUpdate); } // Order the events randomly. The configurations are stored in a separate // buffer, so they might be written before any othe events. Hence, we can't // mix the config events with other events. for (size_t i = config_count; i < event_types.size(); i++) { size_t other = prng.Rand(static_cast(i), static_cast(event_types.size() - 1)); RTC_CHECK(i <= other && other < event_types.size()); std::swap(event_types[i], event_types[other]); } } void RtcEventLogSession::WriteSession() { // Find the name of the current test, in order to use it as a temporary // filename. auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::string test_name = test_info->name(); std::replace(test_name.begin(), test_name.end(), '/', '_'); const std::string temp_filename = test::OutputPath() + "RtcEventLogTest_" + test_name; rtc::ScopedFakeClock fake_clock; fake_clock.SetTimeMicros(prng.Rand()); // When log_dumper goes out of scope, it causes the log file to be flushed // to disk. std::unique_ptr log_dumper( RtcEventLog::Create(RtcEventLog::EncodingType::Legacy)); size_t incoming_rtp_written = 0; size_t outgoing_rtp_written = 0; size_t incoming_rtcp_written = 0; size_t outgoing_rtcp_written = 0; size_t playouts_written = 0; size_t bwe_loss_written = 0; size_t bwe_delay_written = 0; size_t recv_configs_written = 0; size_t send_configs_written = 0; for (size_t i = 0; i < event_types.size(); i++) { fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000)); if (i == event_types.size() / 2) log_dumper->StartLogging( rtc::MakeUnique(temp_filename, 10000000), output_period_ms); switch (event_types[i]) { case EventType::kIncomingRtp: RTC_CHECK(incoming_rtp_written < incoming_rtp_packets.size()); log_dumper->Log(rtc::MakeUnique( incoming_rtp_packets[incoming_rtp_written++])); break; case EventType::kOutgoingRtp: { RTC_CHECK(outgoing_rtp_written < outgoing_rtp_packets.size()); constexpr int kNotAProbe = PacedPacketInfo::kNotAProbe; // Compiler... log_dumper->Log(rtc::MakeUnique( outgoing_rtp_packets[outgoing_rtp_written++], kNotAProbe)); break; } case EventType::kIncomingRtcp: RTC_CHECK(incoming_rtcp_written < incoming_rtcp_packets.size()); log_dumper->Log(rtc::MakeUnique( incoming_rtcp_packets[incoming_rtcp_written++])); break; case EventType::kOutgoingRtcp: RTC_CHECK(outgoing_rtcp_written < outgoing_rtcp_packets.size()); log_dumper->Log(rtc::MakeUnique( outgoing_rtcp_packets[outgoing_rtcp_written++])); break; case EventType::kAudioPlayout: RTC_CHECK(playouts_written < playout_ssrcs.size()); log_dumper->Log(rtc::MakeUnique( playout_ssrcs[playouts_written++])); break; case EventType::kBweLossUpdate: RTC_CHECK(bwe_loss_written < bwe_loss_updates.size()); log_dumper->Log(rtc::MakeUnique( bwe_loss_updates[bwe_loss_written].bitrate_bps, bwe_loss_updates[bwe_loss_written].fraction_loss, bwe_loss_updates[bwe_loss_written].total_packets)); bwe_loss_written++; break; case EventType::kBweDelayUpdate: RTC_CHECK(bwe_delay_written < bwe_delay_updates.size()); log_dumper->Log(rtc::MakeUnique( bwe_delay_updates[bwe_delay_written].first, bwe_delay_updates[bwe_delay_written].second)); bwe_delay_written++; break; case EventType::kVideoRecvConfig: RTC_CHECK(recv_configs_written < receiver_configs.size()); log_dumper->Log(rtc::MakeUnique( rtc::MakeUnique( receiver_configs[recv_configs_written++]))); break; case EventType::kVideoSendConfig: RTC_CHECK(send_configs_written < sender_configs.size()); log_dumper->Log(rtc::MakeUnique( rtc::MakeUnique( sender_configs[send_configs_written++]))); break; case EventType::kAudioRecvConfig: // Not implemented RTC_NOTREACHED(); break; case EventType::kAudioSendConfig: // Not implemented RTC_NOTREACHED(); break; case EventType::kAudioNetworkAdaptation: // Not implemented RTC_NOTREACHED(); break; case EventType::kBweProbeClusterCreated: // Not implemented RTC_NOTREACHED(); break; case EventType::kBweProbeResult: // Not implemented RTC_NOTREACHED(); break; } } log_dumper->StopLogging(); } // Read the file and verify that what we read back from the event log is the // same as what we wrote down. void RtcEventLogSession::ReadAndVerifySession() { // Find the name of the current test, in order to use it as a temporary // filename. auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::string test_name = test_info->name(); std::replace(test_name.begin(), test_name.end(), '/', '_'); const std::string temp_filename = test::OutputPath() + "RtcEventLogTest_" + test_name; // Read the generated file from disk. ParsedRtcEventLog parsed_log; ASSERT_TRUE(parsed_log.ParseFile(temp_filename)); EXPECT_GE(5000u, event_types.size() + 2); // The events must fit. EXPECT_EQ(event_types.size() + 2, parsed_log.GetNumberOfEvents()); size_t incoming_rtp_read = 0; size_t outgoing_rtp_read = 0; size_t incoming_rtcp_read = 0; size_t outgoing_rtcp_read = 0; size_t playouts_read = 0; size_t bwe_loss_read = 0; size_t bwe_delay_read = 0; size_t recv_configs_read = 0; size_t send_configs_read = 0; RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0); for (size_t i = 0; i < event_types.size(); i++) { switch (event_types[i]) { case EventType::kIncomingRtp: RTC_CHECK(incoming_rtp_read < incoming_rtp_packets.size()); RtcEventLogTestHelper::VerifyIncomingRtpEvent( parsed_log, i + 1, incoming_rtp_packets[incoming_rtp_read++]); break; case EventType::kOutgoingRtp: RTC_CHECK(outgoing_rtp_read < outgoing_rtp_packets.size()); RtcEventLogTestHelper::VerifyOutgoingRtpEvent( parsed_log, i + 1, outgoing_rtp_packets[outgoing_rtp_read++]); break; case EventType::kIncomingRtcp: RTC_CHECK(incoming_rtcp_read < incoming_rtcp_packets.size()); RtcEventLogTestHelper::VerifyRtcpEvent( parsed_log, i + 1, kIncomingPacket, incoming_rtcp_packets[incoming_rtcp_read].data(), incoming_rtcp_packets[incoming_rtcp_read].size()); incoming_rtcp_read++; break; case EventType::kOutgoingRtcp: RTC_CHECK(outgoing_rtcp_read < outgoing_rtcp_packets.size()); RtcEventLogTestHelper::VerifyRtcpEvent( parsed_log, i + 1, kOutgoingPacket, outgoing_rtcp_packets[outgoing_rtcp_read].data(), outgoing_rtcp_packets[outgoing_rtcp_read].size()); outgoing_rtcp_read++; break; case EventType::kAudioPlayout: RTC_CHECK(playouts_read < playout_ssrcs.size()); RtcEventLogTestHelper::VerifyPlayoutEvent( parsed_log, i + 1, playout_ssrcs[playouts_read++]); break; case EventType::kBweLossUpdate: RTC_CHECK(bwe_loss_read < bwe_loss_updates.size()); RtcEventLogTestHelper::VerifyBweLossEvent( parsed_log, i + 1, bwe_loss_updates[bwe_loss_read].bitrate_bps, bwe_loss_updates[bwe_loss_read].fraction_loss, bwe_loss_updates[bwe_loss_read].total_packets); bwe_loss_read++; break; case EventType::kBweDelayUpdate: RTC_CHECK(bwe_delay_read < bwe_delay_updates.size()); RtcEventLogTestHelper::VerifyBweDelayEvent( parsed_log, i + 1, bwe_delay_updates[bwe_delay_read].first, bwe_delay_updates[bwe_delay_read].second); bwe_delay_read++; break; case EventType::kVideoRecvConfig: RTC_CHECK(recv_configs_read < receiver_configs.size()); RtcEventLogTestHelper::VerifyVideoReceiveStreamConfig( parsed_log, i + 1, receiver_configs[recv_configs_read++]); break; case EventType::kVideoSendConfig: RTC_CHECK(send_configs_read < sender_configs.size()); RtcEventLogTestHelper::VerifyVideoSendStreamConfig( parsed_log, i + 1, sender_configs[send_configs_read++]); break; case EventType::kAudioRecvConfig: // Not implemented RTC_NOTREACHED(); break; case EventType::kAudioSendConfig: // Not implemented RTC_NOTREACHED(); break; case EventType::kAudioNetworkAdaptation: // Not implemented RTC_NOTREACHED(); break; case EventType::kBweProbeClusterCreated: // Not implemented RTC_NOTREACHED(); break; case EventType::kBweProbeResult: // Not implemented RTC_NOTREACHED(); break; } } RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, parsed_log.GetNumberOfEvents() - 1); // Clean up temporary file - can be pretty slow. remove(temp_filename.c_str()); } void RtcEventLogSession::PrintExpectedEvents(std::ostream& stream) { for (size_t i = 0; i < event_types.size(); i++) { auto it = event_type_to_string.find(event_types[i]); RTC_CHECK(it != event_type_to_string.end()); stream << it->second << " "; } stream << std::endl; } void PrintActualEvents(const ParsedRtcEventLog& parsed_log, std::ostream& stream) { for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) { auto it = parsed_event_type_to_string.find(parsed_log.GetEventType(i)); RTC_CHECK(it != parsed_event_type_to_string.end()); stream << it->second << " "; } stream << std::endl; } TEST_P(RtcEventLogSession, LogSessionAndReadBack) { RtpHeaderExtensionMap extensions; GenerateSessionDescription(3, // Number of incoming RTP packets. 2, // Number of outgoing RTP packets. 1, // Number of incoming RTCP packets. 1, // Number of outgoing RTCP packets. 0, // Number of playout events. 0, // Number of BWE loss events. 0, // Number of BWE delay events. extensions, // No extensions. 0); // Number of contributing sources. WriteSession(); ReadAndVerifySession(); } TEST_P(RtcEventLogSession, LogSessionAndReadBackWith2Extensions) { RtpHeaderExtensionMap extensions; extensions.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId); extensions.Register(kRtpExtensionTransportSequenceNumber, kTransportSequenceNumberExtensionId); GenerateSessionDescription(4, 4, 1, 1, 0, 0, 0, extensions, 0); WriteSession(); ReadAndVerifySession(); } TEST_P(RtcEventLogSession, LogSessionAndReadBackWithAllExtensions) { RtpHeaderExtensionMap extensions; for (uint32_t i = 0; i < kNumExtensions; i++) { extensions.Register(kExtensionTypes[i], kExtensionIds[i]); } GenerateSessionDescription(5, 4, 1, 1, 3, 2, 2, extensions, 2); WriteSession(); ReadAndVerifySession(); } TEST_P(RtcEventLogSession, LogLongSessionAndReadBack) { RtpHeaderExtensionMap extensions; for (uint32_t i = 0; i < kNumExtensions; i++) { extensions.Register(kExtensionTypes[i], kExtensionIds[i]); } GenerateSessionDescription(1000, 1000, 250, 250, 200, 100, 100, extensions, 1); WriteSession(); ReadAndVerifySession(); } TEST(RtcEventLogTest, CircularBufferKeepsMostRecentEvents) { constexpr size_t kNumEvents = 20000; constexpr int64_t kStartTime = 1000000; auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::string test_name = test_info->name(); std::replace(test_name.begin(), test_name.end(), '/', '_'); const std::string temp_filename = test::OutputPath() + "RtcEventLogTest_" + test_name; rtc::ScopedFakeClock fake_clock; fake_clock.SetTimeMicros(kStartTime); // When log_dumper goes out of scope, it causes the log file to be flushed // to disk. std::unique_ptr log_dumper( RtcEventLog::Create(RtcEventLog::EncodingType::Legacy)); for (size_t i = 0; i < kNumEvents; i++) { // The purpose of the test is to verify that the log can handle // more events than what fits in the internal circular buffer. The exact // type of events does not matter so we chose AudioPlayouts for simplicity. // We use the index as an ssrc to get a strict relationship between the ssrc // and the timestamp. We use this for some basic consistency checks when we // read back. log_dumper->Log(rtc::MakeUnique(i)); fake_clock.AdvanceTimeMicros(10000); } log_dumper->StartLogging( rtc::MakeUnique(temp_filename, 10000000), RtcEventLog::kImmediateOutput); log_dumper->StopLogging(); // Read the generated file from disk. ParsedRtcEventLog parsed_log; ASSERT_TRUE(parsed_log.ParseFile(temp_filename)); // If the following fails, it probably means that kNumEvents isn't larger // than the size of the cyclic buffer in the event log. Try increasing // kNumEvents. EXPECT_LT(parsed_log.GetNumberOfEvents(), kNumEvents); // We expect a start event, some number of playouts events and a stop event. EXPECT_GT(parsed_log.GetNumberOfEvents(), 2u); RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0); rtc::Optional last_timestamp; rtc::Optional last_ssrc; for (size_t i = 1; i < parsed_log.GetNumberOfEvents() - 1; i++) { EXPECT_EQ(parsed_log.GetEventType(i), ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT); uint32_t ssrc; parsed_log.GetAudioPlayout(i, &ssrc); int64_t timestamp = parsed_log.GetTimestamp(i); EXPECT_LT(ssrc, kNumEvents); EXPECT_EQ(static_cast(kStartTime + 10000 * ssrc), timestamp); if (last_ssrc) EXPECT_EQ(ssrc, *last_ssrc + 1); if (last_timestamp) EXPECT_EQ(timestamp, *last_timestamp + 10000); last_ssrc = ssrc; last_timestamp = timestamp; } RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, parsed_log.GetNumberOfEvents() - 1); } INSTANTIATE_TEST_CASE_P( RtcEventLogTest, RtcEventLogSession, ::testing::Combine(::testing::Values(1234567, 7654321), ::testing::Values(RtcEventLog::kImmediateOutput, 1, 5))); } // namespace webrtc