Estimate RTP clock frequency and plot capture-send delay.
Bug: webrtc:8450 Change-Id: Idea093854a644f3018a565168425583dc4783ce9 Reviewed-on: https://webrtc-review.googlesource.com/15480 Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Commit-Queue: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20442}
This commit is contained in:

committed by
Commit Bot

parent
30915a742d
commit
0295a967c0
@ -218,6 +218,7 @@ if (!build_with_chromium) {
|
|||||||
"../modules/audio_coding:ana_debug_dump_proto",
|
"../modules/audio_coding:ana_debug_dump_proto",
|
||||||
"../modules/audio_coding:neteq_tools",
|
"../modules/audio_coding:neteq_tools",
|
||||||
"../rtc_base:rtc_base_approved",
|
"../rtc_base:rtc_base_approved",
|
||||||
|
"../rtc_base:rtc_numerics",
|
||||||
|
|
||||||
# TODO(kwiberg): Remove this dependency.
|
# TODO(kwiberg): Remove this dependency.
|
||||||
"../api/audio_codecs:audio_codecs_api",
|
"../api/audio_codecs:audio_codecs_api",
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/format_macros.h"
|
#include "rtc_base/format_macros.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/numerics/sequence_number_util.h"
|
||||||
#include "rtc_base/ptr_util.h"
|
#include "rtc_base/ptr_util.h"
|
||||||
#include "rtc_base/rate_statistics.h"
|
#include "rtc_base/rate_statistics.h"
|
||||||
|
|
||||||
@ -596,6 +597,46 @@ std::string EventLogAnalyzer::GetStreamName(StreamId stream_id) const {
|
|||||||
return name.str();
|
return name.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is much more reliable for outgoing streams than for incoming streams.
|
||||||
|
rtc::Optional<uint32_t> EventLogAnalyzer::EstimateRtpClockFrequency(
|
||||||
|
const std::vector<LoggedRtpPacket>& packets) const {
|
||||||
|
RTC_CHECK(packets.size() >= 2);
|
||||||
|
uint64_t end_time_us = log_segments_.empty()
|
||||||
|
? std::numeric_limits<uint64_t>::max()
|
||||||
|
: log_segments_.front().second;
|
||||||
|
SeqNumUnwrapper<uint32_t> unwrapper;
|
||||||
|
uint64_t first_rtp_timestamp = unwrapper.Unwrap(packets[0].header.timestamp);
|
||||||
|
uint64_t first_log_timestamp = packets[0].timestamp;
|
||||||
|
uint64_t last_rtp_timestamp = first_rtp_timestamp;
|
||||||
|
uint64_t last_log_timestamp = first_log_timestamp;
|
||||||
|
for (size_t i = 1; i < packets.size(); i++) {
|
||||||
|
if (packets[i].timestamp > end_time_us)
|
||||||
|
break;
|
||||||
|
last_rtp_timestamp = unwrapper.Unwrap(packets[i].header.timestamp);
|
||||||
|
last_log_timestamp = packets[i].timestamp;
|
||||||
|
}
|
||||||
|
if (last_log_timestamp - first_log_timestamp < 1000000) {
|
||||||
|
LOG(LS_WARNING)
|
||||||
|
<< "Failed to estimate RTP clock frequency: Stream too short. ("
|
||||||
|
<< packets.size() << " packets, "
|
||||||
|
<< last_log_timestamp - first_log_timestamp << " us)";
|
||||||
|
return rtc::Optional<uint32_t>();
|
||||||
|
}
|
||||||
|
double duration =
|
||||||
|
static_cast<double>(last_log_timestamp - first_log_timestamp) / 1000000;
|
||||||
|
double estimated_frequency =
|
||||||
|
(last_rtp_timestamp - first_rtp_timestamp) / duration;
|
||||||
|
for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) {
|
||||||
|
if (std::fabs(estimated_frequency - f) < 0.05 * f) {
|
||||||
|
return rtc::Optional<uint32_t>(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate "
|
||||||
|
<< estimated_frequency
|
||||||
|
<< "not close to any stardard RTP frequency.";
|
||||||
|
return rtc::Optional<uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
|
void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
|
||||||
Plot* plot) {
|
Plot* plot) {
|
||||||
for (auto& kv : rtp_packets_) {
|
for (auto& kv : rtp_packets_) {
|
||||||
@ -1459,6 +1500,55 @@ std::vector<std::pair<int64_t, int64_t>> EventLogAnalyzer::GetFrameTimestamps()
|
|||||||
return timestamps;
|
return timestamps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) {
|
||||||
|
for (const auto& kv : rtp_packets_) {
|
||||||
|
const std::vector<LoggedRtpPacket>& packets = kv.second;
|
||||||
|
StreamId stream_id = kv.first;
|
||||||
|
|
||||||
|
if (packets.size() < 2) {
|
||||||
|
LOG(LS_WARNING) << "Can't estimate a the RTP clock frequency or the "
|
||||||
|
"pacer delay with less than 2 packets in the stream";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rtc::Optional<uint32_t> estimated_frequency =
|
||||||
|
EstimateRtpClockFrequency(packets);
|
||||||
|
if (!estimated_frequency)
|
||||||
|
continue;
|
||||||
|
if (IsVideoSsrc(stream_id) && *estimated_frequency != 90000) {
|
||||||
|
LOG(LS_WARNING)
|
||||||
|
<< "Video stream should use a 90 kHz clock but appears to use "
|
||||||
|
<< *estimated_frequency / 1000 << ". Discarding.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSeries pacer_delay_series(
|
||||||
|
GetStreamName(stream_id) + "(" +
|
||||||
|
std::to_string(*estimated_frequency / 1000) + " kHz)",
|
||||||
|
LINE_DOT_GRAPH);
|
||||||
|
SeqNumUnwrapper<uint32_t> timestamp_unwrapper;
|
||||||
|
uint64_t first_capture_timestamp =
|
||||||
|
timestamp_unwrapper.Unwrap(packets.front().header.timestamp);
|
||||||
|
uint64_t first_send_timestamp = packets.front().timestamp;
|
||||||
|
for (LoggedRtpPacket packet : packets) {
|
||||||
|
double capture_time_ms = (static_cast<double>(timestamp_unwrapper.Unwrap(
|
||||||
|
packet.header.timestamp)) -
|
||||||
|
first_capture_timestamp) /
|
||||||
|
*estimated_frequency * 1000;
|
||||||
|
double send_time_ms =
|
||||||
|
static_cast<double>(packet.timestamp - first_send_timestamp) / 1000;
|
||||||
|
float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
|
||||||
|
float y = send_time_ms - capture_time_ms;
|
||||||
|
pacer_delay_series.points.emplace_back(x, y);
|
||||||
|
}
|
||||||
|
plot->AppendTimeSeries(std::move(pacer_delay_series));
|
||||||
|
}
|
||||||
|
|
||||||
|
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
|
||||||
|
plot->SetSuggestedYAxis(0, 10, "Pacer delay (ms)", kBottomMargin, kTopMargin);
|
||||||
|
plot->SetTitle(
|
||||||
|
"Delay from capture to send time. (First packet normalized to 0.)");
|
||||||
|
}
|
||||||
|
|
||||||
void EventLogAnalyzer::CreateTimestampGraph(Plot* plot) {
|
void EventLogAnalyzer::CreateTimestampGraph(Plot* plot) {
|
||||||
for (const auto& kv : rtp_packets_) {
|
for (const auto& kv : rtp_packets_) {
|
||||||
const std::vector<LoggedRtpPacket>& rtp_packets = kv.second;
|
const std::vector<LoggedRtpPacket>& rtp_packets = kv.second;
|
||||||
|
@ -94,6 +94,7 @@ class EventLogAnalyzer {
|
|||||||
void CreateReceiveSideBweSimulationGraph(Plot* plot);
|
void CreateReceiveSideBweSimulationGraph(Plot* plot);
|
||||||
|
|
||||||
void CreateNetworkDelayFeedbackGraph(Plot* plot);
|
void CreateNetworkDelayFeedbackGraph(Plot* plot);
|
||||||
|
void CreatePacerDelayGraph(Plot* plot);
|
||||||
void CreateTimestampGraph(Plot* plot);
|
void CreateTimestampGraph(Plot* plot);
|
||||||
|
|
||||||
void CreateAudioEncoderTargetBitrateGraph(Plot* plot);
|
void CreateAudioEncoderTargetBitrateGraph(Plot* plot);
|
||||||
@ -144,7 +145,10 @@ class EventLogAnalyzer {
|
|||||||
|
|
||||||
bool IsAudioSsrc(StreamId stream_id) const;
|
bool IsAudioSsrc(StreamId stream_id) const;
|
||||||
|
|
||||||
std::string GetStreamName(StreamId) const;
|
std::string GetStreamName(StreamId stream_id) const;
|
||||||
|
|
||||||
|
rtc::Optional<uint32_t> EstimateRtpClockFrequency(
|
||||||
|
const std::vector<LoggedRtpPacket>& packets) const;
|
||||||
|
|
||||||
const ParsedRtcEventLog& parsed_log_;
|
const ParsedRtcEventLog& parsed_log_;
|
||||||
|
|
||||||
|
@ -88,6 +88,11 @@ DEFINE_bool(plot_fraction_loss_feedback,
|
|||||||
true,
|
true,
|
||||||
"Plot packet loss in percent for outgoing packets (as perceived by "
|
"Plot packet loss in percent for outgoing packets (as perceived by "
|
||||||
"the send-side bandwidth estimator).");
|
"the send-side bandwidth estimator).");
|
||||||
|
DEFINE_bool(plot_pacer_delay,
|
||||||
|
false,
|
||||||
|
"Plot the time each sent packet has spent in the pacer (based on "
|
||||||
|
"the difference between the RTP timestamp and the send "
|
||||||
|
"timestamp).");
|
||||||
DEFINE_bool(plot_timestamps,
|
DEFINE_bool(plot_timestamps,
|
||||||
false,
|
false,
|
||||||
"Plot the rtp timestamps of all rtp and rtcp packets over time.");
|
"Plot the rtp timestamps of all rtp and rtcp packets over time.");
|
||||||
@ -265,6 +270,9 @@ int main(int argc, char* argv[]) {
|
|||||||
if (FLAG_plot_timestamps) {
|
if (FLAG_plot_timestamps) {
|
||||||
analyzer.CreateTimestampGraph(collection->AppendNewPlot());
|
analyzer.CreateTimestampGraph(collection->AppendNewPlot());
|
||||||
}
|
}
|
||||||
|
if (FLAG_plot_pacer_delay) {
|
||||||
|
analyzer.CreatePacerDelayGraph(collection->AppendNewPlot());
|
||||||
|
}
|
||||||
if (FLAG_plot_audio_encoder_bitrate_bps) {
|
if (FLAG_plot_audio_encoder_bitrate_bps) {
|
||||||
analyzer.CreateAudioEncoderTargetBitrateGraph(collection->AppendNewPlot());
|
analyzer.CreateAudioEncoderTargetBitrateGraph(collection->AppendNewPlot());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user