Reland: Add BWE plot to event log analyzer.
The plot is constructed by actually running the congestion controller with the logged rtp headers and rtcp feedback messages to reproduce the same behavior as in the real call. R=phoglund@webrtc.org, terelius@webrtc.org Review URL: https://codereview.webrtc.org/2193763002 . Cr-Commit-Position: refs/heads/master@{#13574}
This commit is contained in:
parent
3d9c71ad73
commit
13181035bc
@ -38,40 +38,6 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// No-op implementation is used if flag is not set, or in tests.
|
|
||||||
class RtcEventLogNullImpl final : public RtcEventLog {
|
|
||||||
public:
|
|
||||||
bool StartLogging(const std::string& file_name,
|
|
||||||
int64_t max_size_bytes) override {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool StartLogging(rtc::PlatformFile platform_file,
|
|
||||||
int64_t max_size_bytes) override {
|
|
||||||
// The platform_file is open and needs to be closed.
|
|
||||||
if (!rtc::ClosePlatformFile(platform_file)) {
|
|
||||||
LOG(LS_ERROR) << "Can't close file.";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void StopLogging() override {}
|
|
||||||
void LogVideoReceiveStreamConfig(
|
|
||||||
const VideoReceiveStream::Config& config) override {}
|
|
||||||
void LogVideoSendStreamConfig(
|
|
||||||
const VideoSendStream::Config& config) override {}
|
|
||||||
void LogRtpHeader(PacketDirection direction,
|
|
||||||
MediaType media_type,
|
|
||||||
const uint8_t* header,
|
|
||||||
size_t packet_length) override {}
|
|
||||||
void LogRtcpPacket(PacketDirection direction,
|
|
||||||
MediaType media_type,
|
|
||||||
const uint8_t* packet,
|
|
||||||
size_t length) override {}
|
|
||||||
void LogAudioPlayout(uint32_t ssrc) override {}
|
|
||||||
void LogBwePacketLossEvent(int32_t bitrate,
|
|
||||||
uint8_t fraction_loss,
|
|
||||||
int32_t total_packets) override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef ENABLE_RTC_EVENT_LOG
|
#ifdef ENABLE_RTC_EVENT_LOG
|
||||||
|
|
||||||
class RtcEventLogImpl final : public RtcEventLog {
|
class RtcEventLogImpl final : public RtcEventLog {
|
||||||
@ -454,6 +420,15 @@ bool RtcEventLog::ParseRtcEventLog(const std::string& file_name,
|
|||||||
|
|
||||||
#endif // ENABLE_RTC_EVENT_LOG
|
#endif // ENABLE_RTC_EVENT_LOG
|
||||||
|
|
||||||
|
bool RtcEventLogNullImpl::StartLogging(rtc::PlatformFile platform_file,
|
||||||
|
int64_t max_size_bytes) {
|
||||||
|
// The platform_file is open and needs to be closed.
|
||||||
|
if (!rtc::ClosePlatformFile(platform_file)) {
|
||||||
|
LOG(LS_ERROR) << "Can't close file.";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// RtcEventLog member functions.
|
// RtcEventLog member functions.
|
||||||
std::unique_ptr<RtcEventLog> RtcEventLog::Create(const Clock* clock) {
|
std::unique_ptr<RtcEventLog> RtcEventLog::Create(const Clock* clock) {
|
||||||
#ifdef ENABLE_RTC_EVENT_LOG
|
#ifdef ENABLE_RTC_EVENT_LOG
|
||||||
|
|||||||
@ -109,6 +109,34 @@ class RtcEventLog {
|
|||||||
rtclog::EventStream* result);
|
rtclog::EventStream* result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// No-op implementation is used if flag is not set, or in tests.
|
||||||
|
class RtcEventLogNullImpl final : public RtcEventLog {
|
||||||
|
public:
|
||||||
|
bool StartLogging(const std::string& file_name,
|
||||||
|
int64_t max_size_bytes) override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool StartLogging(rtc::PlatformFile platform_file,
|
||||||
|
int64_t max_size_bytes) override;
|
||||||
|
void StopLogging() override {}
|
||||||
|
void LogVideoReceiveStreamConfig(
|
||||||
|
const VideoReceiveStream::Config& config) override {}
|
||||||
|
void LogVideoSendStreamConfig(
|
||||||
|
const VideoSendStream::Config& config) override {}
|
||||||
|
void LogRtpHeader(PacketDirection direction,
|
||||||
|
MediaType media_type,
|
||||||
|
const uint8_t* header,
|
||||||
|
size_t packet_length) override {}
|
||||||
|
void LogRtcpPacket(PacketDirection direction,
|
||||||
|
MediaType media_type,
|
||||||
|
const uint8_t* packet,
|
||||||
|
size_t length) override {}
|
||||||
|
void LogAudioPlayout(uint32_t ssrc) override {}
|
||||||
|
void LogBwePacketLossEvent(int32_t bitrate,
|
||||||
|
uint8_t fraction_loss,
|
||||||
|
int32_t total_packets) override {}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
#endif // WEBRTC_CALL_RTC_EVENT_LOG_H_
|
#endif // WEBRTC_CALL_RTC_EVENT_LOG_H_
|
||||||
|
|||||||
@ -304,7 +304,7 @@ void CongestionController::SignalNetworkState(NetworkState state) {
|
|||||||
|
|
||||||
void CongestionController::OnSentPacket(const rtc::SentPacket& sent_packet) {
|
void CongestionController::OnSentPacket(const rtc::SentPacket& sent_packet) {
|
||||||
transport_feedback_adapter_.OnSentPacket(sent_packet.packet_id,
|
transport_feedback_adapter_.OnSentPacket(sent_packet.packet_id,
|
||||||
sent_packet.send_time_ms);
|
sent_packet.send_time_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CongestionController::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
|
void CongestionController::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
|
||||||
|
|||||||
@ -114,6 +114,7 @@ webrtc_fuzzer_test("congestion_controller_feedback_fuzzer") {
|
|||||||
"congestion_controller_feedback_fuzzer.cc",
|
"congestion_controller_feedback_fuzzer.cc",
|
||||||
]
|
]
|
||||||
deps = [
|
deps = [
|
||||||
|
"../../:rtc_event_log",
|
||||||
"../../modules/congestion_controller/",
|
"../../modules/congestion_controller/",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,42 +26,13 @@ class NullBitrateObserver : public CongestionController::Observer,
|
|||||||
uint32_t bitrate) override {}
|
uint32_t bitrate) override {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullEventLog : public RtcEventLog {
|
|
||||||
public:
|
|
||||||
~NullEventLog() override {}
|
|
||||||
bool StartLogging(const std::string& file_name,
|
|
||||||
int64_t max_size_bytes) override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool StartLogging(rtc::PlatformFile platform_file, int64_t max_size_bytes) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void StopLogging() override{};
|
|
||||||
void LogVideoReceiveStreamConfig(
|
|
||||||
const webrtc::VideoReceiveStream::Config& config) override {}
|
|
||||||
void LogVideoSendStreamConfig(
|
|
||||||
const webrtc::VideoSendStream::Config& config) override {}
|
|
||||||
void LogRtpHeader(PacketDirection direction,
|
|
||||||
MediaType media_type,
|
|
||||||
const uint8_t* header,
|
|
||||||
size_t packet_length) override {}
|
|
||||||
void LogRtcpPacket(PacketDirection direction,
|
|
||||||
MediaType media_type,
|
|
||||||
const uint8_t* packet,
|
|
||||||
size_t length) override {}
|
|
||||||
void LogAudioPlayout(uint32_t ssrc) override {}
|
|
||||||
void LogBwePacketLossEvent(int32_t bitrate,
|
|
||||||
uint8_t fraction_loss,
|
|
||||||
int32_t total_packets) override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
void FuzzOneInput(const uint8_t* data, size_t size) {
|
void FuzzOneInput(const uint8_t* data, size_t size) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
if (size < sizeof(int64_t) + sizeof(uint8_t) + sizeof(uint32_t))
|
if (size < sizeof(int64_t) + sizeof(uint8_t) + sizeof(uint32_t))
|
||||||
return;
|
return;
|
||||||
SimulatedClock clock(data[i++]);
|
SimulatedClock clock(data[i++]);
|
||||||
NullBitrateObserver observer;
|
NullBitrateObserver observer;
|
||||||
NullEventLog event_log;
|
RtcEventLogNullImpl event_log;
|
||||||
CongestionController cc(&clock, &observer, &observer, &event_log);
|
CongestionController cc(&clock, &observer, &observer, &event_log);
|
||||||
RemoteBitrateEstimator* rbe = cc.GetRemoteBitrateEstimator(true);
|
RemoteBitrateEstimator* rbe = cc.GetRemoteBitrateEstimator(true);
|
||||||
RTPHeader header;
|
RTPHeader header;
|
||||||
|
|||||||
@ -173,6 +173,7 @@ source_set("agc_test_utils") {
|
|||||||
if (rtc_include_tests) {
|
if (rtc_include_tests) {
|
||||||
if (rtc_enable_protobuf) {
|
if (rtc_enable_protobuf) {
|
||||||
executable("event_log_visualizer") {
|
executable("event_log_visualizer") {
|
||||||
|
testonly = true
|
||||||
sources = [
|
sources = [
|
||||||
"event_log_visualizer/analyzer.cc",
|
"event_log_visualizer/analyzer.cc",
|
||||||
"event_log_visualizer/analyzer.h",
|
"event_log_visualizer/analyzer.h",
|
||||||
@ -193,9 +194,12 @@ if (rtc_include_tests) {
|
|||||||
|
|
||||||
defines = [ "ENABLE_RTC_EVENT_LOG" ]
|
defines = [ "ENABLE_RTC_EVENT_LOG" ]
|
||||||
deps = [
|
deps = [
|
||||||
|
"../:rtc_event_log",
|
||||||
"../:rtc_event_log_parser",
|
"../:rtc_event_log_parser",
|
||||||
|
"../modules/congestion_controller:congestion_controller",
|
||||||
"../modules/rtp_rtcp:rtp_rtcp",
|
"../modules/rtp_rtcp:rtp_rtcp",
|
||||||
"../system_wrappers:system_wrappers_default",
|
"../system_wrappers:system_wrappers_default",
|
||||||
|
"../test:field_trial",
|
||||||
"//third_party/gflags",
|
"//third_party/gflags",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ include_rules = [
|
|||||||
"+webrtc/call",
|
"+webrtc/call",
|
||||||
"+webrtc/common_video",
|
"+webrtc/common_video",
|
||||||
"+webrtc/modules/audio_processing",
|
"+webrtc/modules/audio_processing",
|
||||||
|
"+webrtc/modules/congestion_controller",
|
||||||
"+webrtc/modules/rtp_rtcp",
|
"+webrtc/modules/rtp_rtcp",
|
||||||
"+webrtc/system_wrappers",
|
"+webrtc/system_wrappers",
|
||||||
"+webrtc/voice_engine",
|
"+webrtc/voice_engine",
|
||||||
|
|||||||
@ -22,9 +22,12 @@
|
|||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/call.h"
|
#include "webrtc/call.h"
|
||||||
#include "webrtc/common_types.h"
|
#include "webrtc/common_types.h"
|
||||||
|
#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
|
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||||
#include "webrtc/video_receive_stream.h"
|
#include "webrtc/video_receive_stream.h"
|
||||||
#include "webrtc/video_send_stream.h"
|
#include "webrtc/video_send_stream.h"
|
||||||
|
|
||||||
@ -92,21 +95,15 @@ bool EventLogAnalyzer::StreamId::operator<(const StreamId& other) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ssrc_ == other.ssrc_) {
|
if (ssrc_ == other.ssrc_) {
|
||||||
if (media_type_ < other.media_type_) {
|
if (direction_ < other.direction_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (media_type_ == other.media_type_) {
|
|
||||||
if (direction_ < other.direction_) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventLogAnalyzer::StreamId::operator==(const StreamId& other) const {
|
bool EventLogAnalyzer::StreamId::operator==(const StreamId& other) const {
|
||||||
return ssrc_ == other.ssrc_ && direction_ == other.direction_ &&
|
return ssrc_ == other.ssrc_ && direction_ == other.direction_;
|
||||||
media_type_ == other.media_type_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,12 +112,11 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
|
uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
|
||||||
uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
|
uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
|
||||||
|
|
||||||
// Maps a stream identifier consisting of ssrc, direction and MediaType
|
// Maps a stream identifier consisting of ssrc and direction
|
||||||
// to the header extensions used by that stream,
|
// to the header extensions used by that stream,
|
||||||
std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
|
std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
|
||||||
|
|
||||||
PacketDirection direction;
|
PacketDirection direction;
|
||||||
MediaType media_type;
|
|
||||||
uint8_t header[IP_PACKET_SIZE];
|
uint8_t header[IP_PACKET_SIZE];
|
||||||
size_t header_length;
|
size_t header_length;
|
||||||
size_t total_length;
|
size_t total_length;
|
||||||
@ -140,8 +136,7 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
|
case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
|
||||||
VideoReceiveStream::Config config(nullptr);
|
VideoReceiveStream::Config config(nullptr);
|
||||||
parsed_log_.GetVideoReceiveConfig(i, &config);
|
parsed_log_.GetVideoReceiveConfig(i, &config);
|
||||||
StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
|
StreamId stream(config.rtp.remote_ssrc, kIncomingPacket);
|
||||||
MediaType::VIDEO);
|
|
||||||
extension_maps[stream].Erase();
|
extension_maps[stream].Erase();
|
||||||
for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
|
for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
|
||||||
const std::string& extension = config.rtp.extensions[j].uri;
|
const std::string& extension = config.rtp.extensions[j].uri;
|
||||||
@ -155,7 +150,7 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
VideoSendStream::Config config(nullptr);
|
VideoSendStream::Config config(nullptr);
|
||||||
parsed_log_.GetVideoSendConfig(i, &config);
|
parsed_log_.GetVideoSendConfig(i, &config);
|
||||||
for (auto ssrc : config.rtp.ssrcs) {
|
for (auto ssrc : config.rtp.ssrcs) {
|
||||||
StreamId stream(ssrc, kOutgoingPacket, MediaType::VIDEO);
|
StreamId stream(ssrc, kOutgoingPacket);
|
||||||
extension_maps[stream].Erase();
|
extension_maps[stream].Erase();
|
||||||
for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
|
for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
|
||||||
const std::string& extension = config.rtp.extensions[j].uri;
|
const std::string& extension = config.rtp.extensions[j].uri;
|
||||||
@ -177,13 +172,14 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ParsedRtcEventLog::RTP_EVENT: {
|
case ParsedRtcEventLog::RTP_EVENT: {
|
||||||
|
MediaType media_type;
|
||||||
parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
|
parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
|
||||||
&header_length, &total_length);
|
&header_length, &total_length);
|
||||||
// Parse header to get SSRC.
|
// Parse header to get SSRC.
|
||||||
RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
|
RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
|
||||||
RTPHeader parsed_header;
|
RTPHeader parsed_header;
|
||||||
rtp_parser.Parse(&parsed_header);
|
rtp_parser.Parse(&parsed_header);
|
||||||
StreamId stream(parsed_header.ssrc, direction, media_type);
|
StreamId stream(parsed_header.ssrc, direction);
|
||||||
// Look up the extension_map and parse it again to get the extensions.
|
// Look up the extension_map and parse it again to get the extensions.
|
||||||
if (extension_maps.count(stream) == 1) {
|
if (extension_maps.count(stream) == 1) {
|
||||||
RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
|
RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
|
||||||
@ -191,10 +187,45 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
}
|
}
|
||||||
uint64_t timestamp = parsed_log_.GetTimestamp(i);
|
uint64_t timestamp = parsed_log_.GetTimestamp(i);
|
||||||
rtp_packets_[stream].push_back(
|
rtp_packets_[stream].push_back(
|
||||||
LoggedRtpPacket(timestamp, parsed_header));
|
LoggedRtpPacket(timestamp, parsed_header, total_length));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ParsedRtcEventLog::RTCP_EVENT: {
|
case ParsedRtcEventLog::RTCP_EVENT: {
|
||||||
|
uint8_t packet[IP_PACKET_SIZE];
|
||||||
|
MediaType media_type;
|
||||||
|
parsed_log_.GetRtcpPacket(i, &direction, &media_type, packet,
|
||||||
|
&total_length);
|
||||||
|
|
||||||
|
RtpUtility::RtpHeaderParser rtp_parser(packet, total_length);
|
||||||
|
RTPHeader parsed_header;
|
||||||
|
RTC_CHECK(rtp_parser.ParseRtcp(&parsed_header));
|
||||||
|
uint32_t ssrc = parsed_header.ssrc;
|
||||||
|
|
||||||
|
RTCPUtility::RTCPParserV2 rtcp_parser(packet, total_length, true);
|
||||||
|
RTC_CHECK(rtcp_parser.IsValid());
|
||||||
|
|
||||||
|
RTCPUtility::RTCPPacketTypes packet_type = rtcp_parser.Begin();
|
||||||
|
while (packet_type != RTCPUtility::RTCPPacketTypes::kInvalid) {
|
||||||
|
switch (packet_type) {
|
||||||
|
case RTCPUtility::RTCPPacketTypes::kTransportFeedback: {
|
||||||
|
// Currently feedback is logged twice, both for audio and video.
|
||||||
|
// Only act on one of them.
|
||||||
|
if (media_type == MediaType::VIDEO) {
|
||||||
|
std::unique_ptr<rtcp::RtcpPacket> rtcp_packet(
|
||||||
|
rtcp_parser.ReleaseRtcpPacket());
|
||||||
|
StreamId stream(ssrc, direction);
|
||||||
|
uint64_t timestamp = parsed_log_.GetTimestamp(i);
|
||||||
|
rtcp_packets_[stream].push_back(LoggedRtcpPacket(
|
||||||
|
timestamp, kRtcpTransportFeedback, std::move(rtcp_packet)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rtcp_parser.Iterate();
|
||||||
|
packet_type = rtcp_parser.PacketType();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ParsedRtcEventLog::LOG_START: {
|
case ParsedRtcEventLog::LOG_START: {
|
||||||
@ -232,6 +263,33 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
|
|||||||
end_time_ = last_timestamp;
|
end_time_ = last_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BitrateObserver : public CongestionController::Observer,
|
||||||
|
public RemoteBitrateObserver {
|
||||||
|
public:
|
||||||
|
BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {}
|
||||||
|
|
||||||
|
void OnNetworkChanged(uint32_t bitrate_bps,
|
||||||
|
uint8_t fraction_loss,
|
||||||
|
int64_t rtt_ms) override {
|
||||||
|
last_bitrate_bps_ = bitrate_bps;
|
||||||
|
bitrate_updated_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
|
||||||
|
uint32_t bitrate) override {}
|
||||||
|
|
||||||
|
uint32_t last_bitrate_bps() const { return last_bitrate_bps_; }
|
||||||
|
bool GetAndResetBitrateUpdated() {
|
||||||
|
bool bitrate_updated = bitrate_updated_;
|
||||||
|
bitrate_updated_ = false;
|
||||||
|
return bitrate_updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t last_bitrate_bps_;
|
||||||
|
bool bitrate_updated_;
|
||||||
|
};
|
||||||
|
|
||||||
void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
|
void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
|
||||||
Plot* plot) {
|
Plot* plot) {
|
||||||
std::map<uint32_t, TimeSeries> time_series;
|
std::map<uint32_t, TimeSeries> time_series;
|
||||||
@ -675,5 +733,113 @@ void EventLogAnalyzer::CreateStreamBitrateGraph(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventLogAnalyzer::CreateBweGraph(Plot* plot) {
|
||||||
|
std::map<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
|
||||||
|
std::map<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
|
||||||
|
|
||||||
|
for (const auto& kv : rtp_packets_) {
|
||||||
|
if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
|
||||||
|
for (const LoggedRtpPacket& rtp_packet : kv.second)
|
||||||
|
outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& kv : rtcp_packets_) {
|
||||||
|
if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
|
||||||
|
for (const LoggedRtcpPacket& rtcp_packet : kv.second)
|
||||||
|
incoming_rtcp.insert(
|
||||||
|
std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SimulatedClock clock(0);
|
||||||
|
BitrateObserver observer;
|
||||||
|
RtcEventLogNullImpl null_event_log;
|
||||||
|
CongestionController cc(&clock, &observer, &observer, &null_event_log);
|
||||||
|
// TODO(holmer): Log the call config and use that here instead.
|
||||||
|
static const uint32_t kDefaultStartBitrateBps = 300000;
|
||||||
|
cc.SetBweBitrates(0, kDefaultStartBitrateBps, -1);
|
||||||
|
|
||||||
|
TimeSeries time_series;
|
||||||
|
time_series.label = "BWE";
|
||||||
|
time_series.style = LINE_DOT_GRAPH;
|
||||||
|
uint32_t max_y = 10;
|
||||||
|
uint32_t min_y = 0;
|
||||||
|
|
||||||
|
auto rtp_iterator = outgoing_rtp.begin();
|
||||||
|
auto rtcp_iterator = incoming_rtcp.begin();
|
||||||
|
|
||||||
|
auto NextRtpTime = [&]() {
|
||||||
|
if (rtp_iterator != outgoing_rtp.end())
|
||||||
|
return static_cast<int64_t>(rtp_iterator->first);
|
||||||
|
return std::numeric_limits<int64_t>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto NextRtcpTime = [&]() {
|
||||||
|
if (rtcp_iterator != incoming_rtcp.end())
|
||||||
|
return static_cast<int64_t>(rtcp_iterator->first);
|
||||||
|
return std::numeric_limits<int64_t>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto NextProcessTime = [&]() {
|
||||||
|
if (rtcp_iterator != incoming_rtcp.end() ||
|
||||||
|
rtp_iterator != outgoing_rtp.end()) {
|
||||||
|
return clock.TimeInMicroseconds() +
|
||||||
|
std::max<int64_t>(cc.TimeUntilNextProcess() * 1000, 0);
|
||||||
|
}
|
||||||
|
return std::numeric_limits<int64_t>::max();
|
||||||
|
};
|
||||||
|
|
||||||
|
int64_t time_us = std::min(NextRtpTime(), NextRtcpTime());
|
||||||
|
while (time_us != std::numeric_limits<int64_t>::max()) {
|
||||||
|
clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
|
||||||
|
if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
|
||||||
|
clock.AdvanceTimeMilliseconds(rtcp_iterator->first / 1000 -
|
||||||
|
clock.TimeInMilliseconds());
|
||||||
|
const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
|
||||||
|
if (rtcp.type == kRtcpTransportFeedback) {
|
||||||
|
cc.GetTransportFeedbackObserver()->OnTransportFeedback(
|
||||||
|
*static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
|
||||||
|
}
|
||||||
|
++rtcp_iterator;
|
||||||
|
}
|
||||||
|
if (clock.TimeInMicroseconds() >= NextRtpTime()) {
|
||||||
|
clock.AdvanceTimeMilliseconds(rtp_iterator->first / 1000 -
|
||||||
|
clock.TimeInMilliseconds());
|
||||||
|
const LoggedRtpPacket& rtp = *rtp_iterator->second;
|
||||||
|
if (rtp.header.extension.hasTransportSequenceNumber) {
|
||||||
|
RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
|
||||||
|
cc.GetTransportFeedbackObserver()->AddPacket(
|
||||||
|
rtp.header.extension.transportSequenceNumber, rtp.total_length, 0);
|
||||||
|
rtc::SentPacket sent_packet(
|
||||||
|
rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
|
||||||
|
cc.OnSentPacket(sent_packet);
|
||||||
|
}
|
||||||
|
++rtp_iterator;
|
||||||
|
}
|
||||||
|
if (clock.TimeInMicroseconds() >= NextProcessTime())
|
||||||
|
cc.Process();
|
||||||
|
if (observer.GetAndResetBitrateUpdated()) {
|
||||||
|
uint32_t y = observer.last_bitrate_bps() / 1000;
|
||||||
|
max_y = std::max(max_y, y);
|
||||||
|
min_y = std::min(min_y, y);
|
||||||
|
float x = static_cast<float>(clock.TimeInMicroseconds() - begin_time_) /
|
||||||
|
1000000;
|
||||||
|
time_series.points.emplace_back(x, y);
|
||||||
|
}
|
||||||
|
time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
|
||||||
|
}
|
||||||
|
// Add the data set to the plot.
|
||||||
|
plot->series.push_back(std::move(time_series));
|
||||||
|
|
||||||
|
plot->xaxis_min = kDefaultXMin;
|
||||||
|
plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
|
||||||
|
plot->xaxis_label = "Time (s)";
|
||||||
|
plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
|
||||||
|
plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
|
||||||
|
plot->yaxis_label = "Bitrate (kbps)";
|
||||||
|
plot->title = "BWE";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace plotting
|
} // namespace plotting
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -13,8 +13,12 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "webrtc/call/rtc_event_log_parser.h"
|
#include "webrtc/call/rtc_event_log_parser.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h"
|
||||||
#include "webrtc/tools/event_log_visualizer/plot_base.h"
|
#include "webrtc/tools/event_log_visualizer/plot_base.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -41,30 +45,41 @@ class EventLogAnalyzer {
|
|||||||
|
|
||||||
void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);
|
void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);
|
||||||
|
|
||||||
|
void CreateBweGraph(Plot* plot);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class StreamId {
|
class StreamId {
|
||||||
public:
|
public:
|
||||||
StreamId(uint32_t ssrc,
|
StreamId(uint32_t ssrc, webrtc::PacketDirection direction)
|
||||||
webrtc::PacketDirection direction,
|
: ssrc_(ssrc), direction_(direction) {}
|
||||||
webrtc::MediaType media_type)
|
|
||||||
: ssrc_(ssrc), direction_(direction), media_type_(media_type) {}
|
|
||||||
bool operator<(const StreamId& other) const;
|
bool operator<(const StreamId& other) const;
|
||||||
bool operator==(const StreamId& other) const;
|
bool operator==(const StreamId& other) const;
|
||||||
uint32_t GetSsrc() const { return ssrc_; }
|
uint32_t GetSsrc() const { return ssrc_; }
|
||||||
webrtc::PacketDirection GetDirection() const { return direction_; }
|
webrtc::PacketDirection GetDirection() const { return direction_; }
|
||||||
webrtc::MediaType GetMediaType() const { return media_type_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t ssrc_;
|
uint32_t ssrc_;
|
||||||
webrtc::PacketDirection direction_;
|
webrtc::PacketDirection direction_;
|
||||||
webrtc::MediaType media_type_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LoggedRtpPacket {
|
struct LoggedRtpPacket {
|
||||||
LoggedRtpPacket(uint64_t timestamp, RTPHeader header)
|
LoggedRtpPacket(uint64_t timestamp, RTPHeader header, size_t total_length)
|
||||||
: timestamp(timestamp), header(header) {}
|
: timestamp(timestamp), header(header), total_length(total_length) {}
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
RTPHeader header;
|
RTPHeader header;
|
||||||
|
size_t total_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoggedRtcpPacket {
|
||||||
|
LoggedRtcpPacket(uint64_t timestamp,
|
||||||
|
RTCPPacketType rtcp_type,
|
||||||
|
std::unique_ptr<rtcp::RtcpPacket> rtcp_packet)
|
||||||
|
: timestamp(timestamp),
|
||||||
|
type(rtcp_type),
|
||||||
|
packet(std::move(rtcp_packet)) {}
|
||||||
|
uint64_t timestamp;
|
||||||
|
RTCPPacketType type;
|
||||||
|
std::unique_ptr<rtcp::RtcpPacket> packet;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BwePacketLossEvent {
|
struct BwePacketLossEvent {
|
||||||
@ -85,6 +100,8 @@ class EventLogAnalyzer {
|
|||||||
// if the stream has been configured.
|
// if the stream has been configured.
|
||||||
std::map<StreamId, std::vector<LoggedRtpPacket>> rtp_packets_;
|
std::map<StreamId, std::vector<LoggedRtpPacket>> rtp_packets_;
|
||||||
|
|
||||||
|
std::map<StreamId, std::vector<LoggedRtcpPacket>> rtcp_packets_;
|
||||||
|
|
||||||
// A list of all updates from the send-side loss-based bandwidth estimator.
|
// A list of all updates from the send-side loss-based bandwidth estimator.
|
||||||
std::vector<BwePacketLossEvent> bwe_loss_updates_;
|
std::vector<BwePacketLossEvent> bwe_loss_updates_;
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,10 @@ DEFINE_bool(plot_total_bitrate,
|
|||||||
DEFINE_bool(plot_stream_bitrate,
|
DEFINE_bool(plot_stream_bitrate,
|
||||||
false,
|
false,
|
||||||
"Plot the bitrate used by each stream.");
|
"Plot the bitrate used by each stream.");
|
||||||
|
DEFINE_bool(plot_bwe,
|
||||||
|
false,
|
||||||
|
"Run the bandwidth estimator with the logged rtp and rtcp and plot "
|
||||||
|
"the output.");
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
std::string program_name = argv[0];
|
std::string program_name = argv[0];
|
||||||
@ -132,6 +136,10 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FLAGS_plot_all || FLAGS_plot_bwe) {
|
||||||
|
analyzer.CreateBweGraph(collection->append_new_plot());
|
||||||
|
}
|
||||||
|
|
||||||
collection->draw();
|
collection->draw();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace plotting {
|
namespace plotting {
|
||||||
|
|
||||||
enum PlotStyle { LINE_GRAPH, BAR_GRAPH };
|
enum PlotStyle { LINE_GRAPH, LINE_DOT_GRAPH, BAR_GRAPH };
|
||||||
|
|
||||||
struct TimeSeriesPoint {
|
struct TimeSeriesPoint {
|
||||||
TimeSeriesPoint(float x, float y) : x(x), y(y) {}
|
TimeSeriesPoint(float x, float y) : x(x), y(y) {}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "webrtc/tools/event_log_visualizer/plot_python.h"
|
#include "webrtc/tools/event_log_visualizer/plot_python.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace plotting {
|
namespace plotting {
|
||||||
@ -58,6 +59,11 @@ void PythonPlot::draw() {
|
|||||||
} else if (series[i].style == LINE_GRAPH) {
|
} else if (series[i].style == LINE_GRAPH) {
|
||||||
printf("plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\')\n", i,
|
printf("plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\')\n", i,
|
||||||
i, i, series[i].label.c_str());
|
i, i, series[i].label.c_str());
|
||||||
|
} else if (series[i].style == LINE_DOT_GRAPH) {
|
||||||
|
printf(
|
||||||
|
"plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\', "
|
||||||
|
"marker='.')\n",
|
||||||
|
i, i, i, series[i].label.c_str());
|
||||||
} else {
|
} else {
|
||||||
printf("raise Exception(\"Unknown graph type\")\n");
|
printf("raise Exception(\"Unknown graph type\")\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,9 +109,12 @@
|
|||||||
'target_name': 'event_log_visualizer',
|
'target_name': 'event_log_visualizer',
|
||||||
'type': 'executable',
|
'type': 'executable',
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
|
'<(webrtc_root)/webrtc.gyp:rtc_event_log',
|
||||||
'<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
|
'<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
|
||||||
|
'<(webrtc_root)/modules/modules.gyp:congestion_controller',
|
||||||
'<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
|
'<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
|
||||||
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
|
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
|
||||||
|
'<(webrtc_root)/test/test.gyp:field_trial',
|
||||||
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
|
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user