| /* | |
| * 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 "modules/audio_coding/neteq/tools/rtc_event_log_source.h" | |
| #include <string.h> | |
| #include <iostream> | |
| #include <limits> | |
| #include <memory> | |
| #include <set> | |
| #include <utility> | |
| #include "absl/strings/string_view.h" | |
| #include "logging/rtc_event_log/rtc_event_processor.h" | |
| #include "modules/audio_coding/neteq/tools/packet.h" | |
| #include "rtc_base/checks.h" | |
| namespace webrtc { | |
| namespace test { | |
| namespace { | |
| bool ShouldSkipStream(ParsedRtcEventLog::MediaType media_type, | |
| uint32_t ssrc, | |
| absl::optional<uint32_t> ssrc_filter) { | |
| if (media_type != ParsedRtcEventLog::MediaType::AUDIO) | |
| return true; | |
| if (ssrc_filter.has_value() && ssrc != *ssrc_filter) | |
| return true; | |
| return false; | |
| } | |
| } // namespace | |
| std::unique_ptr<RtcEventLogSource> RtcEventLogSource::CreateFromFile( | |
| absl::string_view file_name, | |
| absl::optional<uint32_t> ssrc_filter) { | |
| auto source = std::unique_ptr<RtcEventLogSource>(new RtcEventLogSource()); | |
| ParsedRtcEventLog parsed_log; | |
| auto status = parsed_log.ParseFile(file_name); | |
| if (!status.ok()) { | |
| std::cerr << "Failed to parse event log: " << status.message() << std::endl; | |
| std::cerr << "Skipping log." << std::endl; | |
| return nullptr; | |
| } | |
| if (!source->Initialize(parsed_log, ssrc_filter)) { | |
| std::cerr << "Failed to initialize source from event log, skipping." | |
| << std::endl; | |
| return nullptr; | |
| } | |
| return source; | |
| } | |
| std::unique_ptr<RtcEventLogSource> RtcEventLogSource::CreateFromString( | |
| absl::string_view file_contents, | |
| absl::optional<uint32_t> ssrc_filter) { | |
| auto source = std::unique_ptr<RtcEventLogSource>(new RtcEventLogSource()); | |
| ParsedRtcEventLog parsed_log; | |
| auto status = parsed_log.ParseString(file_contents); | |
| if (!status.ok()) { | |
| std::cerr << "Failed to parse event log: " << status.message() << std::endl; | |
| std::cerr << "Skipping log." << std::endl; | |
| return nullptr; | |
| } | |
| if (!source->Initialize(parsed_log, ssrc_filter)) { | |
| std::cerr << "Failed to initialize source from event log, skipping." | |
| << std::endl; | |
| return nullptr; | |
| } | |
| return source; | |
| } | |
| RtcEventLogSource::~RtcEventLogSource() {} | |
| std::unique_ptr<Packet> RtcEventLogSource::NextPacket() { | |
| if (rtp_packet_index_ >= rtp_packets_.size()) | |
| return nullptr; | |
| std::unique_ptr<Packet> packet = std::move(rtp_packets_[rtp_packet_index_++]); | |
| return packet; | |
| } | |
| int64_t RtcEventLogSource::NextAudioOutputEventMs() { | |
| if (audio_output_index_ >= audio_outputs_.size()) | |
| return std::numeric_limits<int64_t>::max(); | |
| int64_t output_time_ms = audio_outputs_[audio_output_index_++]; | |
| return output_time_ms; | |
| } | |
| RtcEventLogSource::RtcEventLogSource() : PacketSource() {} | |
| bool RtcEventLogSource::Initialize(const ParsedRtcEventLog& parsed_log, | |
| absl::optional<uint32_t> ssrc_filter) { | |
| const auto first_log_end_time_us = | |
| parsed_log.stop_log_events().empty() | |
| ? std::numeric_limits<int64_t>::max() | |
| : parsed_log.stop_log_events().front().log_time_us(); | |
| std::set<uint32_t> packet_ssrcs; | |
| auto handle_rtp_packet = | |
| [this, first_log_end_time_us, | |
| &packet_ssrcs](const webrtc::LoggedRtpPacketIncoming& incoming) { | |
| if (!filter_.test(incoming.rtp.header.payloadType) && | |
| incoming.log_time_us() < first_log_end_time_us) { | |
| rtp_packets_.emplace_back(std::make_unique<Packet>( | |
| incoming.rtp.header, incoming.rtp.total_length, | |
| incoming.rtp.total_length - incoming.rtp.header_length, | |
| static_cast<double>(incoming.log_time_ms()))); | |
| packet_ssrcs.insert(rtp_packets_.back()->header().ssrc); | |
| } | |
| }; | |
| std::set<uint32_t> ignored_ssrcs; | |
| auto handle_audio_playout = | |
| [this, first_log_end_time_us, &packet_ssrcs, | |
| &ignored_ssrcs](const webrtc::LoggedAudioPlayoutEvent& audio_playout) { | |
| if (audio_playout.log_time_us() < first_log_end_time_us) { | |
| if (packet_ssrcs.count(audio_playout.ssrc) > 0) { | |
| audio_outputs_.emplace_back(audio_playout.log_time_ms()); | |
| } else { | |
| ignored_ssrcs.insert(audio_playout.ssrc); | |
| } | |
| } | |
| }; | |
| // This wouldn't be needed if we knew that there was at most one audio stream. | |
| webrtc::RtcEventProcessor event_processor; | |
| for (const auto& rtp_packets : parsed_log.incoming_rtp_packets_by_ssrc()) { | |
| ParsedRtcEventLog::MediaType media_type = | |
| parsed_log.GetMediaType(rtp_packets.ssrc, webrtc::kIncomingPacket); | |
| if (ShouldSkipStream(media_type, rtp_packets.ssrc, ssrc_filter)) { | |
| continue; | |
| } | |
| event_processor.AddEvents(rtp_packets.incoming_packets, handle_rtp_packet); | |
| // If no SSRC filter has been set, use the first SSRC only. The simulator | |
| // does not work properly with interleaved packets from multiple SSRCs. | |
| if (!ssrc_filter.has_value()) { | |
| ssrc_filter = rtp_packets.ssrc; | |
| } | |
| } | |
| for (const auto& audio_playouts : parsed_log.audio_playout_events()) { | |
| if (ssrc_filter.has_value() && audio_playouts.first != *ssrc_filter) | |
| continue; | |
| event_processor.AddEvents(audio_playouts.second, handle_audio_playout); | |
| } | |
| // Fills in rtp_packets_ and audio_outputs_. | |
| event_processor.ProcessEventsInOrder(); | |
| for (const auto& ssrc : ignored_ssrcs) { | |
| std::cout << "Ignoring GetAudio events from SSRC 0x" << std::hex << ssrc | |
| << " because no packets were found with a matching SSRC." | |
| << std::endl; | |
| } | |
| return true; | |
| } | |
| } // namespace test | |
| } // namespace webrtc |