| /* |
| * 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 <set> |
| #include <utility> |
| |
| #include "absl/memory/memory.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( |
| const std::string& file_name, |
| absl::optional<uint32_t> ssrc_filter) { |
| auto source = std::unique_ptr<RtcEventLogSource>(new RtcEventLogSource()); |
| ParsedRtcEventLog parsed_log; |
| if (!parsed_log.ParseFile(file_name) || |
| !source->Initialize(parsed_log, ssrc_filter)) { |
| std::cerr << "Error while parsing event log, skipping." << std::endl; |
| return nullptr; |
| } |
| return source; |
| } |
| |
| std::unique_ptr<RtcEventLogSource> RtcEventLogSource::CreateFromString( |
| const std::string& file_contents, |
| absl::optional<uint32_t> ssrc_filter) { |
| auto source = std::unique_ptr<RtcEventLogSource>(new RtcEventLogSource()); |
| ParsedRtcEventLog parsed_log; |
| if (!parsed_log.ParseString(file_contents) || |
| !source->Initialize(parsed_log, ssrc_filter)) { |
| std::cerr << "Error while parsing 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(absl::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); |
| } |
| |
| 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 |