mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 22:00:47 +01:00

This CL fixes 2 issues that affect NetEq simulations. - When using event logs with multiple SSRCs, it does not make sense to use more than a single SSRC. If the user does not provide an SSRC filter, we should use the first SSRC we find and no others. - It is possible for event logs to have a gap in the middle, and sometimes we don't store/mark the gap properly. If is possible to detect gaps by looking at the wallclock time delta between getAudio events. These should be 10 ms nominally, so values greater than 1000 should never happen and indicate an error. Bug: webrtc:11855 Change-Id: Idc3b8a7902be4159da48b063ef5c5c82fd484071 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/181940 Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Commit-Queue: Ivo Creusen <ivoc@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31960}
167 lines
5.8 KiB
C++
167 lines
5.8 KiB
C++
/*
|
|
* 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 "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;
|
|
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(
|
|
const std::string& 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
|