Moving src/webrtc into src/.
In order to eliminate the WebRTC Subtree mirror in Chromium, WebRTC is moving the content of the src/webrtc directory up to the src/ directory. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true TBR=tommi@webrtc.org Bug: chromium:611808 Change-Id: Iac59c5b51b950f174119565bac87955a7994bc38 Reviewed-on: https://webrtc-review.googlesource.com/1560 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Henrik Kjellander <kjellander@webrtc.org> Cr-Commit-Position: refs/heads/master@{#19845}
This commit is contained in:
committed by
Commit Bot
parent
6674846b4a
commit
bb547203bf
196
logging/BUILD.gn
Normal file
196
logging/BUILD.gn
Normal file
@ -0,0 +1,196 @@
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
import("../webrtc.gni")
|
||||
import("//third_party/protobuf/proto_library.gni")
|
||||
if (is_android) {
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/android/rules.gni")
|
||||
}
|
||||
|
||||
group("logging") {
|
||||
public_deps = [
|
||||
":rtc_event_log_impl",
|
||||
]
|
||||
if (rtc_enable_protobuf) {
|
||||
public_deps += [ ":rtc_event_log_parser" ]
|
||||
}
|
||||
}
|
||||
|
||||
rtc_source_set("rtc_event_log_api") {
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log.h",
|
||||
"rtc_event_log/rtc_event_log_factory_interface.h",
|
||||
"rtc_event_log/rtc_stream_config.cc",
|
||||
"rtc_event_log/rtc_stream_config.h",
|
||||
]
|
||||
deps = [
|
||||
"..:webrtc_common",
|
||||
"../api:libjingle_peerconnection_api",
|
||||
"../call:video_stream_api",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_static_library("rtc_event_log_impl") {
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log.cc",
|
||||
"rtc_event_log/rtc_event_log_factory.cc",
|
||||
"rtc_event_log/rtc_event_log_factory.h",
|
||||
]
|
||||
|
||||
defines = []
|
||||
|
||||
deps = [
|
||||
":rtc_event_log_api",
|
||||
"..:webrtc_common",
|
||||
"../modules/audio_coding:audio_network_adaptor",
|
||||
"../modules/remote_bitrate_estimator:remote_bitrate_estimator",
|
||||
"../modules/rtp_rtcp",
|
||||
"../rtc_base:protobuf_utils",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:rtc_task_queue",
|
||||
"../rtc_base:sequenced_task_checker",
|
||||
"../system_wrappers",
|
||||
]
|
||||
|
||||
if (rtc_enable_protobuf) {
|
||||
defines += [ "ENABLE_RTC_EVENT_LOG" ]
|
||||
deps += [ ":rtc_event_log_proto" ]
|
||||
}
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_enable_protobuf) {
|
||||
proto_library("rtc_event_log_proto") {
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log.proto",
|
||||
]
|
||||
proto_out_dir = "webrtc/logging/rtc_event_log"
|
||||
}
|
||||
|
||||
rtc_static_library("rtc_event_log_parser") {
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log_parser.cc",
|
||||
"rtc_event_log/rtc_event_log_parser.h",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
":rtc_event_log_api",
|
||||
":rtc_event_log_proto",
|
||||
"..:webrtc_common",
|
||||
"../modules/audio_coding:audio_network_adaptor",
|
||||
"../modules/remote_bitrate_estimator:remote_bitrate_estimator",
|
||||
"../modules/rtp_rtcp:rtp_rtcp",
|
||||
"../system_wrappers",
|
||||
]
|
||||
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
deps = [
|
||||
"../call:video_stream_api",
|
||||
"../rtc_base:protobuf_utils",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_source_set("rtc_event_log_tests") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log_unittest.cc",
|
||||
"rtc_event_log/rtc_event_log_unittest_helper.cc",
|
||||
"rtc_event_log/rtc_event_log_unittest_helper.h",
|
||||
]
|
||||
deps = [
|
||||
":rtc_event_log_impl",
|
||||
":rtc_event_log_parser",
|
||||
"../call",
|
||||
"../modules/audio_coding:audio_network_adaptor",
|
||||
"../modules/remote_bitrate_estimator:remote_bitrate_estimator",
|
||||
"../modules/rtp_rtcp",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../rtc_base:rtc_base_tests_utils",
|
||||
"../system_wrappers:metrics_default",
|
||||
"../test:test_support",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
]
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
}
|
||||
rtc_test("rtc_event_log2rtp_dump") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log2rtp_dump.cc",
|
||||
]
|
||||
deps = [
|
||||
":rtc_event_log_api",
|
||||
":rtc_event_log_impl",
|
||||
":rtc_event_log_parser",
|
||||
"../modules/rtp_rtcp:rtp_rtcp",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
"../system_wrappers:field_trial_default",
|
||||
"../system_wrappers:metrics_default",
|
||||
"../test:rtp_test_utils",
|
||||
]
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rtc_include_tests) {
|
||||
rtc_executable("rtc_event_log2text") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log2text.cc",
|
||||
]
|
||||
deps = [
|
||||
":rtc_event_log_api",
|
||||
":rtc_event_log_impl",
|
||||
":rtc_event_log_parser",
|
||||
"../call:video_stream_api",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
|
||||
# TODO(kwiberg): Remove this dependency.
|
||||
"../api/audio_codecs:audio_codecs_api",
|
||||
"../modules/rtp_rtcp:rtp_rtcp",
|
||||
]
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rtc_include_tests) {
|
||||
rtc_executable("rtc_event_log2stats") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"rtc_event_log/rtc_event_log2stats.cc",
|
||||
]
|
||||
deps = [
|
||||
":rtc_event_log_api",
|
||||
":rtc_event_log_impl",
|
||||
":rtc_event_log_proto",
|
||||
"../rtc_base:rtc_base_approved",
|
||||
]
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
logging/OWNERS
Normal file
3
logging/OWNERS
Normal file
@ -0,0 +1,3 @@
|
||||
skvlad@webrtc.org
|
||||
stefan@webrtc.org
|
||||
terelius@webrtc.org
|
||||
7
logging/rtc_event_log/DEPS
Normal file
7
logging/rtc_event_log/DEPS
Normal file
@ -0,0 +1,7 @@
|
||||
include_rules = [
|
||||
"+webrtc/call",
|
||||
"+webrtc/modules/audio_coding/audio_network_adaptor",
|
||||
"+webrtc/modules/remote_bitrate_estimator/include",
|
||||
"+webrtc/modules/rtp_rtcp",
|
||||
"+webrtc/system_wrappers",
|
||||
]
|
||||
83
logging/rtc_event_log/mock/mock_rtc_event_log.h
Normal file
83
logging/rtc_event_log/mock/mock_rtc_event_log.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
|
||||
#include "webrtc/test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockRtcEventLog : public RtcEventLog {
|
||||
public:
|
||||
MOCK_METHOD2(StartLogging,
|
||||
bool(const std::string& file_name, int64_t max_size_bytes));
|
||||
|
||||
MOCK_METHOD2(StartLogging,
|
||||
bool(rtc::PlatformFile log_file, int64_t max_size_bytes));
|
||||
|
||||
MOCK_METHOD0(StopLogging, void());
|
||||
|
||||
MOCK_METHOD1(LogVideoReceiveStreamConfig,
|
||||
void(const rtclog::StreamConfig& config));
|
||||
|
||||
MOCK_METHOD1(LogVideoSendStreamConfig,
|
||||
void(const rtclog::StreamConfig& config));
|
||||
|
||||
MOCK_METHOD1(LogAudioReceiveStreamConfig,
|
||||
void(const rtclog::StreamConfig& config));
|
||||
|
||||
MOCK_METHOD1(LogAudioSendStreamConfig,
|
||||
void(const rtclog::StreamConfig& config));
|
||||
|
||||
MOCK_METHOD3(LogRtpHeader,
|
||||
void(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length));
|
||||
|
||||
MOCK_METHOD4(LogRtpHeader,
|
||||
void(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length,
|
||||
int probe_cluster_id));
|
||||
|
||||
MOCK_METHOD3(LogRtcpPacket,
|
||||
void(PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t length));
|
||||
|
||||
MOCK_METHOD1(LogAudioPlayout, void(uint32_t ssrc));
|
||||
|
||||
MOCK_METHOD3(LogLossBasedBweUpdate,
|
||||
void(int32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets));
|
||||
|
||||
MOCK_METHOD2(LogDelayBasedBweUpdate,
|
||||
void(int32_t bitrate_bps, BandwidthUsage detector_state));
|
||||
|
||||
MOCK_METHOD1(LogAudioNetworkAdaptation,
|
||||
void(const AudioEncoderRuntimeConfig& config));
|
||||
|
||||
MOCK_METHOD4(LogProbeClusterCreated,
|
||||
void(int id, int bitrate_bps, int min_probes, int min_bytes));
|
||||
|
||||
MOCK_METHOD2(LogProbeResultSuccess, void(int id, int bitrate_bps));
|
||||
MOCK_METHOD2(LogProbeResultFailure,
|
||||
void(int id, ProbeFailureReason failure_reason));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_
|
||||
849
logging/rtc_event_log/rtc_event_log.cc
Normal file
849
logging/rtc_event_log/rtc_event_log.cc
Normal file
@ -0,0 +1,849 @@
|
||||
/*
|
||||
* 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 "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/constructormagic.h"
|
||||
#include "webrtc/rtc_base/event.h"
|
||||
#include "webrtc/rtc_base/ignore_wundef.h"
|
||||
#include "webrtc/rtc_base/logging.h"
|
||||
#include "webrtc/rtc_base/protobuf_utils.h"
|
||||
#include "webrtc/rtc_base/ptr_util.h"
|
||||
#include "webrtc/rtc_base/sequenced_task_checker.h"
|
||||
#include "webrtc/rtc_base/task_queue.h"
|
||||
#include "webrtc/rtc_base/thread_annotations.h"
|
||||
#include "webrtc/rtc_base/timeutils.h"
|
||||
#include "webrtc/system_wrappers/include/file_wrapper.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
#ifdef ENABLE_RTC_EVENT_LOG
|
||||
// *.pb.h files are generated at build-time by the protobuf compiler.
|
||||
RTC_PUSH_IGNORING_WUNDEF()
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#else
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#endif
|
||||
RTC_POP_IGNORING_WUNDEF()
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
#ifdef ENABLE_RTC_EVENT_LOG
|
||||
|
||||
namespace {
|
||||
const int kEventsInHistory = 10000;
|
||||
|
||||
bool IsConfigEvent(const rtclog::Event& event) {
|
||||
rtclog::Event_EventType event_type = event.type();
|
||||
return event_type == rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT ||
|
||||
event_type == rtclog::Event::VIDEO_SENDER_CONFIG_EVENT ||
|
||||
event_type == rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT ||
|
||||
event_type == rtclog::Event::AUDIO_SENDER_CONFIG_EVENT;
|
||||
}
|
||||
|
||||
// TODO(eladalon): This class exists because C++11 doesn't allow transferring a
|
||||
// unique_ptr to a lambda (a copy constructor is required). We should get
|
||||
// rid of this when we move to C++14.
|
||||
template <typename T>
|
||||
class ResourceOwningTask final : public rtc::QueuedTask {
|
||||
public:
|
||||
ResourceOwningTask(std::unique_ptr<T> resource,
|
||||
std::function<void(std::unique_ptr<T>)> handler)
|
||||
: resource_(std::move(resource)), handler_(handler) {}
|
||||
|
||||
bool Run() override {
|
||||
handler_(std::move(resource_));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T> resource_;
|
||||
std::function<void(std::unique_ptr<T>)> handler_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class RtcEventLogImpl final : public RtcEventLog {
|
||||
friend std::unique_ptr<RtcEventLog> RtcEventLog::Create();
|
||||
|
||||
public:
|
||||
~RtcEventLogImpl() override;
|
||||
|
||||
bool StartLogging(const std::string& file_name,
|
||||
int64_t max_size_bytes) override;
|
||||
bool StartLogging(rtc::PlatformFile platform_file,
|
||||
int64_t max_size_bytes) override;
|
||||
void StopLogging() override;
|
||||
void LogVideoReceiveStreamConfig(const rtclog::StreamConfig& config) override;
|
||||
void LogVideoSendStreamConfig(const rtclog::StreamConfig& config) override;
|
||||
void LogAudioReceiveStreamConfig(const rtclog::StreamConfig& config) override;
|
||||
void LogAudioSendStreamConfig(const rtclog::StreamConfig& config) override;
|
||||
void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length) override;
|
||||
void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length,
|
||||
int probe_cluster_id) override;
|
||||
void LogRtcpPacket(PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t length) override;
|
||||
void LogAudioPlayout(uint32_t ssrc) override;
|
||||
void LogLossBasedBweUpdate(int32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets) override;
|
||||
void LogDelayBasedBweUpdate(int32_t bitrate_bps,
|
||||
BandwidthUsage detector_state) override;
|
||||
void LogAudioNetworkAdaptation(
|
||||
const AudioEncoderRuntimeConfig& config) override;
|
||||
void LogProbeClusterCreated(int id,
|
||||
int bitrate_bps,
|
||||
int min_probes,
|
||||
int min_bytes) override;
|
||||
void LogProbeResultSuccess(int id, int bitrate_bps) override;
|
||||
void LogProbeResultFailure(int id,
|
||||
ProbeFailureReason failure_reason) override;
|
||||
|
||||
private:
|
||||
void StartLoggingInternal(std::unique_ptr<FileWrapper> file,
|
||||
int64_t max_size_bytes);
|
||||
|
||||
RtcEventLogImpl(); // Creation is done by RtcEventLog::Create.
|
||||
|
||||
void StoreEvent(std::unique_ptr<rtclog::Event> event);
|
||||
void LogProbeResult(int id,
|
||||
rtclog::BweProbeResult::ResultType result,
|
||||
int bitrate_bps);
|
||||
|
||||
// Appends an event to the output protobuf string, returning true on success.
|
||||
// Fails and returns false in case the limit on output size prevents the
|
||||
// event from being added; in this case, the output string is left unchanged.
|
||||
bool AppendEventToString(rtclog::Event* event,
|
||||
ProtoString* output_string) RTC_WARN_UNUSED_RESULT;
|
||||
|
||||
void LogToMemory(std::unique_ptr<rtclog::Event> event);
|
||||
|
||||
void StartLogFile();
|
||||
void LogToFile(std::unique_ptr<rtclog::Event> event);
|
||||
void StopLogFile(int64_t stop_time);
|
||||
|
||||
// Observe a limit on the number of concurrent logs, so as not to run into
|
||||
// OS-imposed limits on open files and/or threads/task-queues.
|
||||
// TODO(eladalon): Known issue - there's a race over |log_count_|.
|
||||
static std::atomic<int> log_count_;
|
||||
|
||||
// Make sure that the event log is "managed" - created/destroyed, as well
|
||||
// as started/stopped - from the same thread/task-queue.
|
||||
rtc::SequencedTaskChecker owner_sequence_checker_;
|
||||
|
||||
// History containing all past configuration events.
|
||||
std::vector<std::unique_ptr<rtclog::Event>> config_history_
|
||||
RTC_ACCESS_ON(task_queue_);
|
||||
|
||||
// History containing the most recent (non-configuration) events (~10s).
|
||||
std::deque<std::unique_ptr<rtclog::Event>> history_
|
||||
RTC_ACCESS_ON(task_queue_);
|
||||
|
||||
std::unique_ptr<FileWrapper> file_ RTC_ACCESS_ON(task_queue_);
|
||||
|
||||
size_t max_size_bytes_ RTC_ACCESS_ON(task_queue_);
|
||||
size_t written_bytes_ RTC_ACCESS_ON(task_queue_);
|
||||
|
||||
// Keep this last to ensure it destructs first, or else tasks living on the
|
||||
// queue might access other members after they've been torn down.
|
||||
rtc::TaskQueue task_queue_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl);
|
||||
};
|
||||
|
||||
namespace {
|
||||
// The functions in this namespace convert enums from the runtime format
|
||||
// that the rest of the WebRtc project can use, to the corresponding
|
||||
// serialized enum which is defined by the protobuf.
|
||||
|
||||
rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) {
|
||||
switch (rtcp_mode) {
|
||||
case RtcpMode::kCompound:
|
||||
return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
|
||||
case RtcpMode::kReducedSize:
|
||||
return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE;
|
||||
case RtcpMode::kOff:
|
||||
RTC_NOTREACHED();
|
||||
return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return rtclog::VideoReceiveConfig::RTCP_COMPOUND;
|
||||
}
|
||||
|
||||
rtclog::DelayBasedBweUpdate::DetectorState ConvertDetectorState(
|
||||
BandwidthUsage state) {
|
||||
switch (state) {
|
||||
case BandwidthUsage::kBwNormal:
|
||||
return rtclog::DelayBasedBweUpdate::BWE_NORMAL;
|
||||
case BandwidthUsage::kBwUnderusing:
|
||||
return rtclog::DelayBasedBweUpdate::BWE_UNDERUSING;
|
||||
case BandwidthUsage::kBwOverusing:
|
||||
return rtclog::DelayBasedBweUpdate::BWE_OVERUSING;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return rtclog::DelayBasedBweUpdate::BWE_NORMAL;
|
||||
}
|
||||
|
||||
rtclog::BweProbeResult::ResultType ConvertProbeResultType(
|
||||
ProbeFailureReason failure_reason) {
|
||||
switch (failure_reason) {
|
||||
case kInvalidSendReceiveInterval:
|
||||
return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL;
|
||||
case kInvalidSendReceiveRatio:
|
||||
return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO;
|
||||
case kTimeout:
|
||||
return rtclog::BweProbeResult::TIMEOUT;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return rtclog::BweProbeResult::SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::atomic<int> RtcEventLogImpl::log_count_(0);
|
||||
|
||||
RtcEventLogImpl::RtcEventLogImpl()
|
||||
: file_(FileWrapper::Create()),
|
||||
max_size_bytes_(std::numeric_limits<decltype(max_size_bytes_)>::max()),
|
||||
written_bytes_(0),
|
||||
task_queue_("rtc_event_log") {}
|
||||
|
||||
RtcEventLogImpl::~RtcEventLogImpl() {
|
||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
|
||||
|
||||
// If we're logging to the file, this will stop that. Blocking function.
|
||||
StopLogging();
|
||||
|
||||
int count = std::atomic_fetch_sub(&RtcEventLogImpl::log_count_, 1) - 1;
|
||||
RTC_DCHECK_GE(count, 0);
|
||||
}
|
||||
|
||||
bool RtcEventLogImpl::StartLogging(const std::string& file_name,
|
||||
int64_t max_size_bytes) {
|
||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
|
||||
|
||||
auto file = rtc::WrapUnique<FileWrapper>(FileWrapper::Create());
|
||||
if (!file->OpenFile(file_name.c_str(), false)) {
|
||||
LOG(LS_ERROR) << "Can't open file. WebRTC event log not started.";
|
||||
return false;
|
||||
}
|
||||
|
||||
StartLoggingInternal(std::move(file), max_size_bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtcEventLogImpl::StartLogging(rtc::PlatformFile platform_file,
|
||||
int64_t max_size_bytes) {
|
||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
|
||||
|
||||
auto file = rtc::WrapUnique<FileWrapper>(FileWrapper::Create());
|
||||
FILE* file_handle = rtc::FdopenPlatformFileForWriting(platform_file);
|
||||
if (!file_handle) {
|
||||
LOG(LS_ERROR) << "Can't open file. WebRTC event log not started.";
|
||||
// Even though we failed to open a FILE*, the platform_file is still open
|
||||
// and needs to be closed.
|
||||
if (!rtc::ClosePlatformFile(platform_file)) {
|
||||
LOG(LS_ERROR) << "Can't close file.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!file->OpenFromFileHandle(file_handle)) {
|
||||
LOG(LS_ERROR) << "Can't open file. WebRTC event log not started.";
|
||||
return false;
|
||||
}
|
||||
|
||||
StartLoggingInternal(std::move(file), max_size_bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::StopLogging() {
|
||||
RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);
|
||||
|
||||
LOG(LS_INFO) << "Stopping WebRTC event log.";
|
||||
|
||||
const int64_t stop_time = rtc::TimeMicros();
|
||||
|
||||
rtc::Event file_finished(true, false);
|
||||
|
||||
task_queue_.PostTask([this, stop_time, &file_finished]() {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
if (file_->is_open()) {
|
||||
StopLogFile(stop_time);
|
||||
}
|
||||
file_finished.Set();
|
||||
});
|
||||
|
||||
file_finished.Wait(rtc::Event::kForever);
|
||||
|
||||
LOG(LS_INFO) << "WebRTC event log successfully stopped.";
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogVideoReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT);
|
||||
|
||||
rtclog::VideoReceiveConfig* receiver_config =
|
||||
event->mutable_video_receiver_config();
|
||||
receiver_config->set_remote_ssrc(config.remote_ssrc);
|
||||
receiver_config->set_local_ssrc(config.local_ssrc);
|
||||
|
||||
// TODO(perkj): Add field for rsid.
|
||||
receiver_config->set_rtcp_mode(ConvertRtcpMode(config.rtcp_mode));
|
||||
receiver_config->set_remb(config.remb);
|
||||
|
||||
for (const auto& e : config.rtp_extensions) {
|
||||
rtclog::RtpHeaderExtension* extension =
|
||||
receiver_config->add_header_extensions();
|
||||
extension->set_name(e.uri);
|
||||
extension->set_id(e.id);
|
||||
}
|
||||
|
||||
for (const auto& d : config.codecs) {
|
||||
rtclog::DecoderConfig* decoder = receiver_config->add_decoders();
|
||||
decoder->set_name(d.payload_name);
|
||||
decoder->set_payload_type(d.payload_type);
|
||||
if (d.rtx_payload_type != 0) {
|
||||
rtclog::RtxMap* rtx = receiver_config->add_rtx_map();
|
||||
rtx->set_payload_type(d.payload_type);
|
||||
rtx->mutable_config()->set_rtx_ssrc(config.rtx_ssrc);
|
||||
rtx->mutable_config()->set_rtx_payload_type(d.rtx_payload_type);
|
||||
}
|
||||
}
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogVideoSendStreamConfig(
|
||||
const rtclog::StreamConfig& config) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT);
|
||||
|
||||
rtclog::VideoSendConfig* sender_config = event->mutable_video_sender_config();
|
||||
|
||||
// TODO(perkj): rtclog::VideoSendConfig should only contain one SSRC.
|
||||
sender_config->add_ssrcs(config.local_ssrc);
|
||||
if (config.rtx_ssrc != 0) {
|
||||
sender_config->add_rtx_ssrcs(config.rtx_ssrc);
|
||||
}
|
||||
|
||||
for (const auto& e : config.rtp_extensions) {
|
||||
rtclog::RtpHeaderExtension* extension =
|
||||
sender_config->add_header_extensions();
|
||||
extension->set_name(e.uri);
|
||||
extension->set_id(e.id);
|
||||
}
|
||||
|
||||
// TODO(perkj): rtclog::VideoSendConfig should contain many possible codec
|
||||
// configurations.
|
||||
for (const auto& codec : config.codecs) {
|
||||
sender_config->set_rtx_payload_type(codec.rtx_payload_type);
|
||||
rtclog::EncoderConfig* encoder = sender_config->mutable_encoder();
|
||||
encoder->set_name(codec.payload_name);
|
||||
encoder->set_payload_type(codec.payload_type);
|
||||
|
||||
if (config.codecs.size() > 1) {
|
||||
LOG(WARNING) << "LogVideoSendStreamConfig currently only supports one "
|
||||
<< "codec. Logging codec :" << codec.payload_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogAudioReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT);
|
||||
|
||||
rtclog::AudioReceiveConfig* receiver_config =
|
||||
event->mutable_audio_receiver_config();
|
||||
receiver_config->set_remote_ssrc(config.remote_ssrc);
|
||||
receiver_config->set_local_ssrc(config.local_ssrc);
|
||||
|
||||
for (const auto& e : config.rtp_extensions) {
|
||||
rtclog::RtpHeaderExtension* extension =
|
||||
receiver_config->add_header_extensions();
|
||||
extension->set_name(e.uri);
|
||||
extension->set_id(e.id);
|
||||
}
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogAudioSendStreamConfig(
|
||||
const rtclog::StreamConfig& config) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::AUDIO_SENDER_CONFIG_EVENT);
|
||||
|
||||
rtclog::AudioSendConfig* sender_config = event->mutable_audio_sender_config();
|
||||
|
||||
sender_config->set_ssrc(config.local_ssrc);
|
||||
|
||||
for (const auto& e : config.rtp_extensions) {
|
||||
rtclog::RtpHeaderExtension* extension =
|
||||
sender_config->add_header_extensions();
|
||||
extension->set_name(e.uri);
|
||||
extension->set_id(e.id);
|
||||
}
|
||||
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length) {
|
||||
LogRtpHeader(direction, header, packet_length, PacedPacketInfo::kNotAProbe);
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length,
|
||||
int probe_cluster_id) {
|
||||
// Read header length (in bytes) from packet data.
|
||||
if (packet_length < 12u) {
|
||||
return; // Don't read outside the packet.
|
||||
}
|
||||
const bool x = (header[0] & 0x10) != 0;
|
||||
const uint8_t cc = header[0] & 0x0f;
|
||||
size_t header_length = 12u + cc * 4u;
|
||||
|
||||
if (x) {
|
||||
if (packet_length < 12u + cc * 4u + 4u) {
|
||||
return; // Don't read outside the packet.
|
||||
}
|
||||
size_t x_len = ByteReader<uint16_t>::ReadBigEndian(header + 14 + cc * 4);
|
||||
header_length += (x_len + 1) * 4;
|
||||
}
|
||||
|
||||
std::unique_ptr<rtclog::Event> rtp_event(new rtclog::Event());
|
||||
rtp_event->set_timestamp_us(rtc::TimeMicros());
|
||||
rtp_event->set_type(rtclog::Event::RTP_EVENT);
|
||||
rtp_event->mutable_rtp_packet()->set_incoming(direction == kIncomingPacket);
|
||||
rtp_event->mutable_rtp_packet()->set_packet_length(packet_length);
|
||||
rtp_event->mutable_rtp_packet()->set_header(header, header_length);
|
||||
if (probe_cluster_id != PacedPacketInfo::kNotAProbe)
|
||||
rtp_event->mutable_rtp_packet()->set_probe_cluster_id(probe_cluster_id);
|
||||
StoreEvent(std::move(rtp_event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogRtcpPacket(PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t length) {
|
||||
std::unique_ptr<rtclog::Event> rtcp_event(new rtclog::Event());
|
||||
rtcp_event->set_timestamp_us(rtc::TimeMicros());
|
||||
rtcp_event->set_type(rtclog::Event::RTCP_EVENT);
|
||||
rtcp_event->mutable_rtcp_packet()->set_incoming(direction == kIncomingPacket);
|
||||
|
||||
rtcp::CommonHeader header;
|
||||
const uint8_t* block_begin = packet;
|
||||
const uint8_t* packet_end = packet + length;
|
||||
RTC_DCHECK(length <= IP_PACKET_SIZE);
|
||||
uint8_t buffer[IP_PACKET_SIZE];
|
||||
uint32_t buffer_length = 0;
|
||||
while (block_begin < packet_end) {
|
||||
if (!header.Parse(block_begin, packet_end - block_begin)) {
|
||||
break; // Incorrect message header.
|
||||
}
|
||||
const uint8_t* next_block = header.NextPacket();
|
||||
uint32_t block_size = next_block - block_begin;
|
||||
switch (header.type()) {
|
||||
case rtcp::SenderReport::kPacketType:
|
||||
case rtcp::ReceiverReport::kPacketType:
|
||||
case rtcp::Bye::kPacketType:
|
||||
case rtcp::ExtendedJitterReport::kPacketType:
|
||||
case rtcp::Rtpfb::kPacketType:
|
||||
case rtcp::Psfb::kPacketType:
|
||||
case rtcp::ExtendedReports::kPacketType:
|
||||
// We log sender reports, receiver reports, bye messages
|
||||
// inter-arrival jitter, third-party loss reports, payload-specific
|
||||
// feedback and extended reports.
|
||||
memcpy(buffer + buffer_length, block_begin, block_size);
|
||||
buffer_length += block_size;
|
||||
break;
|
||||
case rtcp::Sdes::kPacketType:
|
||||
case rtcp::App::kPacketType:
|
||||
default:
|
||||
// We don't log sender descriptions, application defined messages
|
||||
// or message blocks of unknown type.
|
||||
break;
|
||||
}
|
||||
|
||||
block_begin += block_size;
|
||||
}
|
||||
rtcp_event->mutable_rtcp_packet()->set_packet_data(buffer, buffer_length);
|
||||
StoreEvent(std::move(rtcp_event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogAudioPlayout(uint32_t ssrc) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT);
|
||||
auto playout_event = event->mutable_audio_playout_event();
|
||||
playout_event->set_local_ssrc(ssrc);
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogLossBasedBweUpdate(int32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::LOSS_BASED_BWE_UPDATE);
|
||||
auto bwe_event = event->mutable_loss_based_bwe_update();
|
||||
bwe_event->set_bitrate_bps(bitrate_bps);
|
||||
bwe_event->set_fraction_loss(fraction_loss);
|
||||
bwe_event->set_total_packets(total_packets);
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogDelayBasedBweUpdate(int32_t bitrate_bps,
|
||||
BandwidthUsage detector_state) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::DELAY_BASED_BWE_UPDATE);
|
||||
auto bwe_event = event->mutable_delay_based_bwe_update();
|
||||
bwe_event->set_bitrate_bps(bitrate_bps);
|
||||
bwe_event->set_detector_state(ConvertDetectorState(detector_state));
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogAudioNetworkAdaptation(
|
||||
const AudioEncoderRuntimeConfig& config) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT);
|
||||
auto audio_network_adaptation = event->mutable_audio_network_adaptation();
|
||||
if (config.bitrate_bps)
|
||||
audio_network_adaptation->set_bitrate_bps(*config.bitrate_bps);
|
||||
if (config.frame_length_ms)
|
||||
audio_network_adaptation->set_frame_length_ms(*config.frame_length_ms);
|
||||
if (config.uplink_packet_loss_fraction) {
|
||||
audio_network_adaptation->set_uplink_packet_loss_fraction(
|
||||
*config.uplink_packet_loss_fraction);
|
||||
}
|
||||
if (config.enable_fec)
|
||||
audio_network_adaptation->set_enable_fec(*config.enable_fec);
|
||||
if (config.enable_dtx)
|
||||
audio_network_adaptation->set_enable_dtx(*config.enable_dtx);
|
||||
if (config.num_channels)
|
||||
audio_network_adaptation->set_num_channels(*config.num_channels);
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogProbeClusterCreated(int id,
|
||||
int bitrate_bps,
|
||||
int min_probes,
|
||||
int min_bytes) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT);
|
||||
|
||||
auto probe_cluster = event->mutable_probe_cluster();
|
||||
probe_cluster->set_id(id);
|
||||
probe_cluster->set_bitrate_bps(bitrate_bps);
|
||||
probe_cluster->set_min_packets(min_probes);
|
||||
probe_cluster->set_min_bytes(min_bytes);
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogProbeResultSuccess(int id, int bitrate_bps) {
|
||||
LogProbeResult(id, rtclog::BweProbeResult::SUCCESS, bitrate_bps);
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogProbeResultFailure(int id,
|
||||
ProbeFailureReason failure_reason) {
|
||||
rtclog::BweProbeResult::ResultType result =
|
||||
ConvertProbeResultType(failure_reason);
|
||||
LogProbeResult(id, result, -1);
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogProbeResult(int id,
|
||||
rtclog::BweProbeResult::ResultType result,
|
||||
int bitrate_bps) {
|
||||
std::unique_ptr<rtclog::Event> event(new rtclog::Event());
|
||||
event->set_timestamp_us(rtc::TimeMicros());
|
||||
event->set_type(rtclog::Event::BWE_PROBE_RESULT_EVENT);
|
||||
|
||||
auto probe_result = event->mutable_probe_result();
|
||||
probe_result->set_id(id);
|
||||
probe_result->set_result(result);
|
||||
if (result == rtclog::BweProbeResult::SUCCESS)
|
||||
probe_result->set_bitrate_bps(bitrate_bps);
|
||||
StoreEvent(std::move(event));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::StartLoggingInternal(std::unique_ptr<FileWrapper> file,
|
||||
int64_t max_size_bytes) {
|
||||
LOG(LS_INFO) << "Starting WebRTC event log.";
|
||||
|
||||
max_size_bytes = (max_size_bytes <= 0)
|
||||
? std::numeric_limits<decltype(max_size_bytes)>::max()
|
||||
: max_size_bytes;
|
||||
auto file_handler = [this,
|
||||
max_size_bytes](std::unique_ptr<FileWrapper> file) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
if (!file_->is_open()) {
|
||||
max_size_bytes_ = max_size_bytes;
|
||||
file_ = std::move(file);
|
||||
StartLogFile();
|
||||
} else {
|
||||
// Already started. Ignore message and close file handle.
|
||||
file->CloseFile();
|
||||
}
|
||||
};
|
||||
task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<FileWrapper>>(
|
||||
std::move(file), file_handler));
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::StoreEvent(std::unique_ptr<rtclog::Event> event) {
|
||||
RTC_DCHECK(event);
|
||||
|
||||
auto event_handler = [this](std::unique_ptr<rtclog::Event> rtclog_event) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
if (file_->is_open()) {
|
||||
LogToFile(std::move(rtclog_event));
|
||||
} else {
|
||||
LogToMemory(std::move(rtclog_event));
|
||||
}
|
||||
};
|
||||
|
||||
task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<rtclog::Event>>(
|
||||
std::move(event), event_handler));
|
||||
}
|
||||
|
||||
bool RtcEventLogImpl::AppendEventToString(rtclog::Event* event,
|
||||
ProtoString* output_string) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
|
||||
// Even though we're only serializing a single event during this call, what
|
||||
// we intend to get is a list of events, with a tag and length preceding
|
||||
// each actual event. To produce that, we serialize a list of a single event.
|
||||
// If we later serialize additional events, the resulting ProtoString will
|
||||
// be a proper concatenation of all those events.
|
||||
|
||||
rtclog::EventStream event_stream;
|
||||
event_stream.add_stream();
|
||||
|
||||
// As a tweak, we swap the new event into the event-stream, write that to
|
||||
// file, then swap back. This saves on some copying.
|
||||
rtclog::Event* output_event = event_stream.mutable_stream(0);
|
||||
output_event->Swap(event);
|
||||
|
||||
bool appended;
|
||||
size_t potential_new_size =
|
||||
written_bytes_ + output_string->size() + event_stream.ByteSize();
|
||||
if (potential_new_size <= max_size_bytes_) {
|
||||
event_stream.AppendToString(output_string);
|
||||
appended = true;
|
||||
} else {
|
||||
appended = false;
|
||||
}
|
||||
|
||||
// When the function returns, the original Event will be unchanged.
|
||||
output_event->Swap(event);
|
||||
|
||||
return appended;
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogToMemory(std::unique_ptr<rtclog::Event> event) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
RTC_DCHECK(!file_->is_open());
|
||||
|
||||
if (IsConfigEvent(*event.get())) {
|
||||
config_history_.push_back(std::move(event));
|
||||
} else {
|
||||
history_.push_back(std::move(event));
|
||||
if (history_.size() > kEventsInHistory) {
|
||||
history_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::StartLogFile() {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
RTC_DCHECK(file_->is_open());
|
||||
|
||||
ProtoString output_string;
|
||||
|
||||
// Create and serialize the LOG_START event.
|
||||
// The timestamp used will correspond to when logging has started. The log
|
||||
// may contain events earlier than the LOG_START event. (In general, the
|
||||
// timestamps in the log are not monotonic.)
|
||||
rtclog::Event start_event;
|
||||
start_event.set_timestamp_us(rtc::TimeMicros());
|
||||
start_event.set_type(rtclog::Event::LOG_START);
|
||||
bool appended = AppendEventToString(&start_event, &output_string);
|
||||
|
||||
// Serialize the config information for all old streams, including streams
|
||||
// which were already logged to previous files.
|
||||
for (auto& event : config_history_) {
|
||||
if (!appended) {
|
||||
break;
|
||||
}
|
||||
appended = AppendEventToString(event.get(), &output_string);
|
||||
}
|
||||
|
||||
// Serialize the events in the event queue.
|
||||
while (appended && !history_.empty()) {
|
||||
appended = AppendEventToString(history_.front().get(), &output_string);
|
||||
if (appended) {
|
||||
// Known issue - if writing to the file fails, these events will have
|
||||
// been lost. If we try to open a new file, these events will be missing
|
||||
// from it.
|
||||
history_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file.
|
||||
if (!file_->Write(output_string.data(), output_string.size())) {
|
||||
LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
|
||||
// The current FileWrapper implementation closes the file on error.
|
||||
RTC_DCHECK(!file_->is_open());
|
||||
return;
|
||||
}
|
||||
written_bytes_ += output_string.size();
|
||||
|
||||
if (!appended) {
|
||||
RTC_DCHECK(file_->is_open());
|
||||
StopLogFile(rtc::TimeMicros());
|
||||
}
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::LogToFile(std::unique_ptr<rtclog::Event> event) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
RTC_DCHECK(file_->is_open());
|
||||
|
||||
ProtoString output_string;
|
||||
|
||||
bool appended = AppendEventToString(event.get(), &output_string);
|
||||
|
||||
if (IsConfigEvent(*event.get())) {
|
||||
config_history_.push_back(std::move(event));
|
||||
}
|
||||
|
||||
if (!appended) {
|
||||
RTC_DCHECK(file_->is_open());
|
||||
history_.push_back(std::move(event));
|
||||
StopLogFile(rtc::TimeMicros());
|
||||
return;
|
||||
}
|
||||
|
||||
// Write string to file.
|
||||
if (file_->Write(output_string.data(), output_string.size())) {
|
||||
written_bytes_ += output_string.size();
|
||||
} else {
|
||||
LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
|
||||
// The current FileWrapper implementation closes the file on error.
|
||||
RTC_DCHECK(!file_->is_open());
|
||||
}
|
||||
}
|
||||
|
||||
void RtcEventLogImpl::StopLogFile(int64_t stop_time) {
|
||||
RTC_DCHECK_RUN_ON(&task_queue_);
|
||||
RTC_DCHECK(file_->is_open());
|
||||
|
||||
ProtoString output_string;
|
||||
|
||||
rtclog::Event end_event;
|
||||
end_event.set_timestamp_us(stop_time);
|
||||
end_event.set_type(rtclog::Event::LOG_END);
|
||||
bool appended = AppendEventToString(&end_event, &output_string);
|
||||
|
||||
if (appended) {
|
||||
if (!file_->Write(output_string.data(), output_string.size())) {
|
||||
LOG(LS_ERROR) << "FileWrapper failed to write WebRtcEventLog file.";
|
||||
// The current FileWrapper implementation closes the file on error.
|
||||
RTC_DCHECK(!file_->is_open());
|
||||
}
|
||||
written_bytes_ += output_string.size();
|
||||
}
|
||||
|
||||
max_size_bytes_ = std::numeric_limits<decltype(max_size_bytes_)>::max();
|
||||
written_bytes_ = 0;
|
||||
|
||||
file_->CloseFile();
|
||||
RTC_DCHECK(!file_->is_open());
|
||||
}
|
||||
|
||||
bool RtcEventLog::ParseRtcEventLog(const std::string& file_name,
|
||||
rtclog::EventStream* result) {
|
||||
char tmp_buffer[1024];
|
||||
int bytes_read = 0;
|
||||
std::unique_ptr<FileWrapper> dump_file(FileWrapper::Create());
|
||||
if (!dump_file->OpenFile(file_name.c_str(), true)) {
|
||||
return false;
|
||||
}
|
||||
ProtoString dump_buffer;
|
||||
while ((bytes_read = dump_file->Read(tmp_buffer, sizeof(tmp_buffer))) > 0) {
|
||||
dump_buffer.append(tmp_buffer, bytes_read);
|
||||
}
|
||||
dump_file->CloseFile();
|
||||
return result->ParseFromString(dump_buffer);
|
||||
}
|
||||
|
||||
#endif // ENABLE_RTC_EVENT_LOG
|
||||
|
||||
// RtcEventLog member functions.
|
||||
std::unique_ptr<RtcEventLog> RtcEventLog::Create() {
|
||||
#ifdef ENABLE_RTC_EVENT_LOG
|
||||
// TODO(eladalon): Known issue - there's a race over |log_count_| here.
|
||||
constexpr int kMaxLogCount = 5;
|
||||
int count = 1 + std::atomic_fetch_add(&RtcEventLogImpl::log_count_, 1);
|
||||
if (count > kMaxLogCount) {
|
||||
LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. "
|
||||
<< count - 1 << " logs open already.";
|
||||
std::atomic_fetch_sub(&RtcEventLogImpl::log_count_, 1);
|
||||
return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl());
|
||||
}
|
||||
return std::unique_ptr<RtcEventLog>(new RtcEventLogImpl());
|
||||
#else
|
||||
return CreateNull();
|
||||
#endif // ENABLE_RTC_EVENT_LOG
|
||||
}
|
||||
|
||||
std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() {
|
||||
return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
206
logging/rtc_event_log/rtc_event_log.h
Normal file
206
logging/rtc_event_log/rtc_event_log.h
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// TODO(eladalon): Get rid of this later in the CL-stack.
|
||||
#include "webrtc/api/rtpparameters.h"
|
||||
#include "webrtc/common_types.h"
|
||||
// TODO(eladalon): Get rid of this later in the CL-stack.
|
||||
#include "webrtc/logging/rtc_event_log/rtc_stream_config.h"
|
||||
#include "webrtc/rtc_base/platform_file.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace rtclog {
|
||||
class EventStream; // Storage class automatically generated from protobuf.
|
||||
} // namespace rtclog
|
||||
|
||||
class Clock;
|
||||
struct AudioEncoderRuntimeConfig;
|
||||
|
||||
enum class MediaType;
|
||||
enum class BandwidthUsage;
|
||||
|
||||
enum PacketDirection { kIncomingPacket = 0, kOutgoingPacket };
|
||||
enum ProbeFailureReason {
|
||||
kInvalidSendReceiveInterval,
|
||||
kInvalidSendReceiveRatio,
|
||||
kTimeout
|
||||
};
|
||||
|
||||
class RtcEventLog {
|
||||
public:
|
||||
virtual ~RtcEventLog() {}
|
||||
|
||||
// Factory method to create an RtcEventLog object.
|
||||
static std::unique_ptr<RtcEventLog> Create();
|
||||
// TODO(nisse): webrtc::Clock is deprecated. Delete this method and
|
||||
// above forward declaration of Clock when
|
||||
// webrtc/system_wrappers/include/clock.h is deleted.
|
||||
static std::unique_ptr<RtcEventLog> Create(const Clock* clock) {
|
||||
return Create();
|
||||
}
|
||||
|
||||
// Create an RtcEventLog object that does nothing.
|
||||
static std::unique_ptr<RtcEventLog> CreateNull();
|
||||
|
||||
// Starts logging a maximum of max_size_bytes bytes to the specified file.
|
||||
// If the file already exists it will be overwritten.
|
||||
// If max_size_bytes <= 0, logging will be active until StopLogging is called.
|
||||
// The function has no effect and returns false if we can't start a new log
|
||||
// e.g. because we are already logging or the file cannot be opened.
|
||||
virtual bool StartLogging(const std::string& file_name,
|
||||
int64_t max_size_bytes) = 0;
|
||||
|
||||
// Same as above. The RtcEventLog takes ownership of the file if the call
|
||||
// is successful, i.e. if it returns true.
|
||||
virtual bool StartLogging(rtc::PlatformFile platform_file,
|
||||
int64_t max_size_bytes) = 0;
|
||||
|
||||
// Deprecated. Pass an explicit file size limit.
|
||||
RTC_DEPRECATED bool StartLogging(const std::string& file_name) {
|
||||
return StartLogging(file_name, 10000000);
|
||||
}
|
||||
|
||||
// Deprecated. Pass an explicit file size limit.
|
||||
RTC_DEPRECATED bool StartLogging(rtc::PlatformFile platform_file) {
|
||||
return StartLogging(platform_file, 10000000);
|
||||
}
|
||||
|
||||
// Stops logging to file and waits until the file has been closed, after
|
||||
// which it would be permissible to read and/or modify it.
|
||||
virtual void StopLogging() = 0;
|
||||
|
||||
// Logs configuration information for a video receive stream.
|
||||
virtual void LogVideoReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) = 0;
|
||||
|
||||
// Logs configuration information for a video send stream.
|
||||
virtual void LogVideoSendStreamConfig(const rtclog::StreamConfig& config) = 0;
|
||||
|
||||
// Logs configuration information for an audio receive stream.
|
||||
virtual void LogAudioReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) = 0;
|
||||
|
||||
// Logs configuration information for an audio send stream.
|
||||
virtual void LogAudioSendStreamConfig(const rtclog::StreamConfig& config) = 0;
|
||||
|
||||
// Logs the header of an incoming or outgoing RTP packet. packet_length
|
||||
// is the total length of the packet, including both header and payload.
|
||||
virtual void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length) = 0;
|
||||
|
||||
// Same as above but used on the sender side to log packets that are part of
|
||||
// a probe cluster.
|
||||
virtual void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length,
|
||||
int probe_cluster_id) = 0;
|
||||
|
||||
// Logs an incoming or outgoing RTCP packet.
|
||||
virtual void LogRtcpPacket(PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t length) = 0;
|
||||
|
||||
// Logs an audio playout event.
|
||||
virtual void LogAudioPlayout(uint32_t ssrc) = 0;
|
||||
|
||||
// Logs a bitrate update from the bandwidth estimator based on packet loss.
|
||||
virtual void LogLossBasedBweUpdate(int32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets) = 0;
|
||||
|
||||
// Logs a bitrate update from the bandwidth estimator based on delay changes.
|
||||
virtual void LogDelayBasedBweUpdate(int32_t bitrate_bps,
|
||||
BandwidthUsage detector_state) = 0;
|
||||
|
||||
// Logs audio encoder re-configuration driven by audio network adaptor.
|
||||
virtual void LogAudioNetworkAdaptation(
|
||||
const AudioEncoderRuntimeConfig& config) = 0;
|
||||
|
||||
// Logs when a probe cluster is created.
|
||||
virtual void LogProbeClusterCreated(int id,
|
||||
int bitrate_bps,
|
||||
int min_probes,
|
||||
int min_bytes) = 0;
|
||||
|
||||
// Logs the result of a successful probing attempt.
|
||||
virtual void LogProbeResultSuccess(int id, int bitrate_bps) = 0;
|
||||
|
||||
// Logs the result of an unsuccessful probing attempt.
|
||||
virtual void LogProbeResultFailure(int id,
|
||||
ProbeFailureReason failure_reason) = 0;
|
||||
|
||||
// Reads an RtcEventLog file and returns true when reading was successful.
|
||||
// The result is stored in the given EventStream object.
|
||||
// The order of the events in the EventStream is implementation defined.
|
||||
// The current implementation writes a LOG_START event, then the old
|
||||
// configurations, then the remaining events in timestamp order and finally
|
||||
// a LOG_END event. However, this might change without further notice.
|
||||
// TODO(terelius): Change result type to a vector?
|
||||
static bool ParseRtcEventLog(const std::string& file_name,
|
||||
rtclog::EventStream* result);
|
||||
};
|
||||
|
||||
// No-op implementation is used if flag is not set, or in tests.
|
||||
class RtcEventLogNullImpl : 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 {
|
||||
return false;
|
||||
}
|
||||
void StopLogging() override {}
|
||||
void LogVideoReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) override {}
|
||||
void LogVideoSendStreamConfig(const rtclog::StreamConfig& config) override {}
|
||||
void LogAudioReceiveStreamConfig(
|
||||
const rtclog::StreamConfig& config) override {}
|
||||
void LogAudioSendStreamConfig(const rtclog::StreamConfig& config) override {}
|
||||
void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length) override {}
|
||||
void LogRtpHeader(PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t packet_length,
|
||||
int probe_cluster_id) override {}
|
||||
void LogRtcpPacket(PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t length) override {}
|
||||
void LogAudioPlayout(uint32_t ssrc) override {}
|
||||
void LogLossBasedBweUpdate(int32_t bitrate_bps,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets) override {}
|
||||
void LogDelayBasedBweUpdate(int32_t bitrate_bps,
|
||||
BandwidthUsage detector_state) override {}
|
||||
void LogAudioNetworkAdaptation(
|
||||
const AudioEncoderRuntimeConfig& config) override {}
|
||||
void LogProbeClusterCreated(int id,
|
||||
int bitrate_bps,
|
||||
int min_probes,
|
||||
int min_bytes) override{};
|
||||
void LogProbeResultSuccess(int id, int bitrate_bps) override{};
|
||||
void LogProbeResultFailure(int id,
|
||||
ProbeFailureReason failure_reason) override{};
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_H_
|
||||
316
logging/rtc_event_log/rtc_event_log.proto
Normal file
316
logging/rtc_event_log/rtc_event_log.proto
Normal file
@ -0,0 +1,316 @@
|
||||
syntax = "proto2";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
package webrtc.rtclog;
|
||||
|
||||
enum MediaType {
|
||||
ANY = 0;
|
||||
AUDIO = 1;
|
||||
VIDEO = 2;
|
||||
DATA = 3;
|
||||
}
|
||||
|
||||
// This is the main message to dump to a file, it can contain multiple event
|
||||
// messages, but it is possible to append multiple EventStreams (each with a
|
||||
// single event) to a file.
|
||||
// This has the benefit that there's no need to keep all data in memory.
|
||||
message EventStream {
|
||||
repeated Event stream = 1;
|
||||
}
|
||||
|
||||
message Event {
|
||||
// required - Elapsed wallclock time in us since the start of the log.
|
||||
optional int64 timestamp_us = 1;
|
||||
|
||||
// The different types of events that can occur, the UNKNOWN_EVENT entry
|
||||
// is added in case future EventTypes are added, in that case old code will
|
||||
// receive the new events as UNKNOWN_EVENT.
|
||||
enum EventType {
|
||||
UNKNOWN_EVENT = 0;
|
||||
LOG_START = 1;
|
||||
LOG_END = 2;
|
||||
RTP_EVENT = 3;
|
||||
RTCP_EVENT = 4;
|
||||
AUDIO_PLAYOUT_EVENT = 5;
|
||||
LOSS_BASED_BWE_UPDATE = 6;
|
||||
DELAY_BASED_BWE_UPDATE = 7;
|
||||
VIDEO_RECEIVER_CONFIG_EVENT = 8;
|
||||
VIDEO_SENDER_CONFIG_EVENT = 9;
|
||||
AUDIO_RECEIVER_CONFIG_EVENT = 10;
|
||||
AUDIO_SENDER_CONFIG_EVENT = 11;
|
||||
AUDIO_NETWORK_ADAPTATION_EVENT = 16;
|
||||
BWE_PROBE_CLUSTER_CREATED_EVENT = 17;
|
||||
BWE_PROBE_RESULT_EVENT = 18;
|
||||
}
|
||||
|
||||
// required - Indicates the type of this event
|
||||
optional EventType type = 2;
|
||||
|
||||
oneof subtype {
|
||||
// required if type == RTP_EVENT
|
||||
RtpPacket rtp_packet = 3;
|
||||
|
||||
// required if type == RTCP_EVENT
|
||||
RtcpPacket rtcp_packet = 4;
|
||||
|
||||
// required if type == AUDIO_PLAYOUT_EVENT
|
||||
AudioPlayoutEvent audio_playout_event = 5;
|
||||
|
||||
// required if type == LOSS_BASED_BWE_UPDATE
|
||||
LossBasedBweUpdate loss_based_bwe_update = 6;
|
||||
|
||||
// required if type == DELAY_BASED_BWE_UPDATE
|
||||
DelayBasedBweUpdate delay_based_bwe_update = 7;
|
||||
|
||||
// required if type == VIDEO_RECEIVER_CONFIG_EVENT
|
||||
VideoReceiveConfig video_receiver_config = 8;
|
||||
|
||||
// required if type == VIDEO_SENDER_CONFIG_EVENT
|
||||
VideoSendConfig video_sender_config = 9;
|
||||
|
||||
// required if type == AUDIO_RECEIVER_CONFIG_EVENT
|
||||
AudioReceiveConfig audio_receiver_config = 10;
|
||||
|
||||
// required if type == AUDIO_SENDER_CONFIG_EVENT
|
||||
AudioSendConfig audio_sender_config = 11;
|
||||
|
||||
// required if type == AUDIO_NETWORK_ADAPTATION_EVENT
|
||||
AudioNetworkAdaptation audio_network_adaptation = 16;
|
||||
|
||||
// required if type == BWE_PROBE_CLUSTER_CREATED_EVENT
|
||||
BweProbeCluster probe_cluster = 17;
|
||||
|
||||
// required if type == BWE_PROBE_RESULT_EVENT
|
||||
BweProbeResult probe_result = 18;
|
||||
}
|
||||
}
|
||||
|
||||
message RtpPacket {
|
||||
// required - True if the packet is incoming w.r.t. the user logging the data
|
||||
optional bool incoming = 1;
|
||||
|
||||
optional MediaType type = 2 [deprecated = true];
|
||||
|
||||
// required - The size of the packet including both payload and header.
|
||||
optional uint32 packet_length = 3;
|
||||
|
||||
// required - The RTP header only.
|
||||
optional bytes header = 4;
|
||||
|
||||
// optional - The probe cluster id.
|
||||
optional uint32 probe_cluster_id = 5;
|
||||
|
||||
// Do not add code to log user payload data without a privacy review!
|
||||
}
|
||||
|
||||
message RtcpPacket {
|
||||
// required - True if the packet is incoming w.r.t. the user logging the data
|
||||
optional bool incoming = 1;
|
||||
|
||||
optional MediaType type = 2 [deprecated = true];
|
||||
|
||||
// required - The whole packet including both payload and header.
|
||||
optional bytes packet_data = 3;
|
||||
}
|
||||
|
||||
message AudioPlayoutEvent {
|
||||
// TODO(ivoc): Rename, we currently use the "remote" ssrc, i.e. identifying
|
||||
// the receive stream, while local_ssrc identifies the send stream, if any.
|
||||
// required - The SSRC of the audio stream associated with the playout event.
|
||||
optional uint32 local_ssrc = 2;
|
||||
}
|
||||
|
||||
message LossBasedBweUpdate {
|
||||
// required - Bandwidth estimate (in bps) after the update.
|
||||
optional int32 bitrate_bps = 1;
|
||||
|
||||
// required - Fraction of lost packets since last receiver report
|
||||
// computed as floor( 256 * (#lost_packets / #total_packets) ).
|
||||
// The possible values range from 0 to 255.
|
||||
optional uint32 fraction_loss = 2;
|
||||
|
||||
// TODO(terelius): Is this really needed? Remove or make optional?
|
||||
// required - Total number of packets that the BWE update is based on.
|
||||
optional int32 total_packets = 3;
|
||||
}
|
||||
|
||||
message DelayBasedBweUpdate {
|
||||
enum DetectorState {
|
||||
BWE_NORMAL = 0;
|
||||
BWE_UNDERUSING = 1;
|
||||
BWE_OVERUSING = 2;
|
||||
}
|
||||
|
||||
// required - Bandwidth estimate (in bps) after the update.
|
||||
optional int32 bitrate_bps = 1;
|
||||
|
||||
// required - The state of the overuse detector.
|
||||
optional DetectorState detector_state = 2;
|
||||
}
|
||||
|
||||
// TODO(terelius): Video and audio streams could in principle share SSRC,
|
||||
// so identifying a stream based only on SSRC might not work.
|
||||
// It might be better to use a combination of SSRC and media type
|
||||
// or SSRC and port number, but for now we will rely on SSRC only.
|
||||
message VideoReceiveConfig {
|
||||
// required - Synchronization source (stream identifier) to be received.
|
||||
optional uint32 remote_ssrc = 1;
|
||||
// required - Sender SSRC used for sending RTCP (such as receiver reports).
|
||||
optional uint32 local_ssrc = 2;
|
||||
|
||||
// Compound mode is described by RFC 4585 and reduced-size
|
||||
// RTCP mode is described by RFC 5506.
|
||||
enum RtcpMode {
|
||||
RTCP_COMPOUND = 1;
|
||||
RTCP_REDUCEDSIZE = 2;
|
||||
}
|
||||
// required - RTCP mode to use.
|
||||
optional RtcpMode rtcp_mode = 3;
|
||||
|
||||
// required - Receiver estimated maximum bandwidth.
|
||||
optional bool remb = 4;
|
||||
|
||||
// Map from video RTP payload type -> RTX config.
|
||||
repeated RtxMap rtx_map = 5;
|
||||
|
||||
// RTP header extensions used for the received stream.
|
||||
repeated RtpHeaderExtension header_extensions = 6;
|
||||
|
||||
// List of decoders associated with the stream.
|
||||
repeated DecoderConfig decoders = 7;
|
||||
}
|
||||
|
||||
// Maps decoder names to payload types.
|
||||
message DecoderConfig {
|
||||
// required
|
||||
optional string name = 1;
|
||||
|
||||
// required
|
||||
optional int32 payload_type = 2;
|
||||
}
|
||||
|
||||
// Maps RTP header extension names to numerical IDs.
|
||||
message RtpHeaderExtension {
|
||||
// required
|
||||
optional string name = 1;
|
||||
|
||||
// required
|
||||
optional int32 id = 2;
|
||||
}
|
||||
|
||||
// RTX settings for incoming video payloads that may be received.
|
||||
// RTX is disabled if there's no config present.
|
||||
message RtxConfig {
|
||||
// required - SSRC to use for the RTX stream.
|
||||
optional uint32 rtx_ssrc = 1;
|
||||
|
||||
// required - Payload type to use for the RTX stream.
|
||||
optional int32 rtx_payload_type = 2;
|
||||
}
|
||||
|
||||
message RtxMap {
|
||||
// required
|
||||
optional int32 payload_type = 1;
|
||||
|
||||
// required
|
||||
optional RtxConfig config = 2;
|
||||
}
|
||||
|
||||
message VideoSendConfig {
|
||||
// Synchronization source (stream identifier) for outgoing stream.
|
||||
// One stream can have several ssrcs for e.g. simulcast.
|
||||
// At least one ssrc is required.
|
||||
repeated uint32 ssrcs = 1;
|
||||
|
||||
// RTP header extensions used for the outgoing stream.
|
||||
repeated RtpHeaderExtension header_extensions = 2;
|
||||
|
||||
// List of SSRCs for retransmitted packets.
|
||||
repeated uint32 rtx_ssrcs = 3;
|
||||
|
||||
// required if rtx_ssrcs is used - Payload type for retransmitted packets.
|
||||
optional int32 rtx_payload_type = 4;
|
||||
|
||||
// required - Encoder associated with the stream.
|
||||
optional EncoderConfig encoder = 5;
|
||||
}
|
||||
|
||||
// Maps encoder names to payload types.
|
||||
message EncoderConfig {
|
||||
// required
|
||||
optional string name = 1;
|
||||
|
||||
// required
|
||||
optional int32 payload_type = 2;
|
||||
}
|
||||
|
||||
message AudioReceiveConfig {
|
||||
// required - Synchronization source (stream identifier) to be received.
|
||||
optional uint32 remote_ssrc = 1;
|
||||
|
||||
// required - Sender SSRC used for sending RTCP (such as receiver reports).
|
||||
optional uint32 local_ssrc = 2;
|
||||
|
||||
// RTP header extensions used for the received audio stream.
|
||||
repeated RtpHeaderExtension header_extensions = 3;
|
||||
}
|
||||
|
||||
message AudioSendConfig {
|
||||
// required - Synchronization source (stream identifier) for outgoing stream.
|
||||
optional uint32 ssrc = 1;
|
||||
|
||||
// RTP header extensions used for the outgoing audio stream.
|
||||
repeated RtpHeaderExtension header_extensions = 2;
|
||||
}
|
||||
|
||||
message AudioNetworkAdaptation {
|
||||
// Bit rate that the audio encoder is operating at.
|
||||
optional int32 bitrate_bps = 1;
|
||||
|
||||
// Frame length that each encoded audio packet consists of.
|
||||
optional int32 frame_length_ms = 2;
|
||||
|
||||
// Packet loss fraction that the encoder's forward error correction (FEC) is
|
||||
// optimized for.
|
||||
optional float uplink_packet_loss_fraction = 3;
|
||||
|
||||
// Whether forward error correction (FEC) is turned on or off.
|
||||
optional bool enable_fec = 4;
|
||||
|
||||
// Whether discontinuous transmission (DTX) is turned on or off.
|
||||
optional bool enable_dtx = 5;
|
||||
|
||||
// Number of audio channels that each encoded packet consists of.
|
||||
optional uint32 num_channels = 6;
|
||||
}
|
||||
|
||||
message BweProbeCluster {
|
||||
// required - The id of this probe cluster.
|
||||
optional uint32 id = 1;
|
||||
|
||||
// required - The bitrate in bps that this probe cluster is meant to probe.
|
||||
optional uint64 bitrate_bps = 2;
|
||||
|
||||
// required - The minimum number of packets used to probe the given bitrate.
|
||||
optional uint32 min_packets = 3;
|
||||
|
||||
// required - The minimum number of bytes used to probe the given bitrate.
|
||||
optional uint32 min_bytes = 4;
|
||||
}
|
||||
|
||||
message BweProbeResult {
|
||||
// required - The id of this probe cluster.
|
||||
optional uint32 id = 1;
|
||||
|
||||
enum ResultType {
|
||||
SUCCESS = 0;
|
||||
INVALID_SEND_RECEIVE_INTERVAL = 1;
|
||||
INVALID_SEND_RECEIVE_RATIO = 2;
|
||||
TIMEOUT = 3;
|
||||
}
|
||||
|
||||
// required - The result of this probing attempt.
|
||||
optional ResultType result = 2;
|
||||
|
||||
// optional - but required if result == SUCCESS. The resulting bitrate in bps.
|
||||
optional uint64 bitrate_bps = 3;
|
||||
}
|
||||
207
logging/rtc_event_log/rtc_event_log2rtp_dump.cc
Normal file
207
logging/rtc_event_log/rtc_event_log2rtp_dump.cc
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_parser.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/flags.h"
|
||||
#include "webrtc/test/rtp_file_writer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using MediaType = webrtc::ParsedRtcEventLog::MediaType;
|
||||
|
||||
DEFINE_bool(
|
||||
audio,
|
||||
true,
|
||||
"Use --noaudio to exclude audio packets from the converted RTPdump file.");
|
||||
DEFINE_bool(
|
||||
video,
|
||||
true,
|
||||
"Use --novideo to exclude video packets from the converted RTPdump file.");
|
||||
DEFINE_bool(
|
||||
data,
|
||||
true,
|
||||
"Use --nodata to exclude data packets from the converted RTPdump file.");
|
||||
DEFINE_bool(
|
||||
rtp,
|
||||
true,
|
||||
"Use --nortp to exclude RTP packets from the converted RTPdump file.");
|
||||
DEFINE_bool(
|
||||
rtcp,
|
||||
true,
|
||||
"Use --nortcp to exclude RTCP packets from the converted RTPdump file.");
|
||||
DEFINE_string(ssrc,
|
||||
"",
|
||||
"Store only packets with this SSRC (decimal or hex, the latter "
|
||||
"starting with 0x).");
|
||||
DEFINE_bool(help, false, "Prints this message.");
|
||||
|
||||
// Parses the input string for a valid SSRC. If a valid SSRC is found, it is
|
||||
// written to the output variable |ssrc|, and true is returned. Otherwise,
|
||||
// false is returned.
|
||||
// The empty string must be validated as true, because it is the default value
|
||||
// of the command-line flag. In this case, no value is written to the output
|
||||
// variable.
|
||||
bool ParseSsrc(std::string str, uint32_t* ssrc) {
|
||||
// If the input string starts with 0x or 0X it indicates a hexadecimal number.
|
||||
auto read_mode = std::dec;
|
||||
if (str.size() > 2 &&
|
||||
(str.substr(0, 2) == "0x" || str.substr(0, 2) == "0X")) {
|
||||
read_mode = std::hex;
|
||||
str = str.substr(2);
|
||||
}
|
||||
std::stringstream ss(str);
|
||||
ss >> read_mode >> *ssrc;
|
||||
return str.empty() || (!ss.fail() && ss.eof());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// This utility will convert a stored event log to the rtpdump format.
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage =
|
||||
"Tool for converting an RtcEventLog file to an RTP dump file.\n"
|
||||
"Run " +
|
||||
program_name +
|
||||
" --help for usage.\n"
|
||||
"Example usage:\n" +
|
||||
program_name + " input.rel output.rtp\n";
|
||||
if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) ||
|
||||
FLAG_help || argc != 3) {
|
||||
std::cout << usage;
|
||||
if (FLAG_help) {
|
||||
rtc::FlagList::Print(nullptr, false);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string input_file = argv[1];
|
||||
std::string output_file = argv[2];
|
||||
|
||||
uint32_t ssrc_filter = 0;
|
||||
if (strlen(FLAG_ssrc) > 0)
|
||||
RTC_CHECK(ParseSsrc(FLAG_ssrc, &ssrc_filter))
|
||||
<< "Flag verification has failed.";
|
||||
|
||||
webrtc::ParsedRtcEventLog parsed_stream;
|
||||
if (!parsed_stream.ParseFile(input_file)) {
|
||||
std::cerr << "Error while parsing input file: " << input_file << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::test::RtpFileWriter> rtp_writer(
|
||||
webrtc::test::RtpFileWriter::Create(
|
||||
webrtc::test::RtpFileWriter::FileFormat::kRtpDump, output_file));
|
||||
|
||||
if (!rtp_writer.get()) {
|
||||
std::cerr << "Error while opening output file: " << output_file
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Found " << parsed_stream.GetNumberOfEvents()
|
||||
<< " events in the input file." << std::endl;
|
||||
int rtp_counter = 0, rtcp_counter = 0;
|
||||
bool header_only = false;
|
||||
for (size_t i = 0; i < parsed_stream.GetNumberOfEvents(); i++) {
|
||||
// The parsed_stream will assert if the protobuf event is missing
|
||||
// some required fields and we attempt to access them. We could consider
|
||||
// a softer failure option, but it does not seem useful to generate
|
||||
// RTP dumps based on broken event logs.
|
||||
if (FLAG_rtp &&
|
||||
parsed_stream.GetEventType(i) == webrtc::ParsedRtcEventLog::RTP_EVENT) {
|
||||
webrtc::test::RtpPacket packet;
|
||||
webrtc::PacketDirection direction;
|
||||
parsed_stream.GetRtpHeader(i, &direction, packet.data, &packet.length,
|
||||
&packet.original_length);
|
||||
if (packet.original_length > packet.length)
|
||||
header_only = true;
|
||||
packet.time_ms = parsed_stream.GetTimestamp(i) / 1000;
|
||||
|
||||
webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet.data,
|
||||
packet.length);
|
||||
|
||||
// TODO(terelius): Maybe add a flag to dump outgoing traffic instead?
|
||||
if (direction == webrtc::kOutgoingPacket)
|
||||
continue;
|
||||
|
||||
webrtc::RTPHeader parsed_header;
|
||||
rtp_parser.Parse(&parsed_header);
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(parsed_header.ssrc, direction);
|
||||
if (!FLAG_audio && media_type == MediaType::AUDIO)
|
||||
continue;
|
||||
if (!FLAG_video && media_type == MediaType::VIDEO)
|
||||
continue;
|
||||
if (!FLAG_data && media_type == MediaType::DATA)
|
||||
continue;
|
||||
if (strlen(FLAG_ssrc) > 0) {
|
||||
const uint32_t packet_ssrc =
|
||||
webrtc::ByteReader<uint32_t>::ReadBigEndian(
|
||||
reinterpret_cast<const uint8_t*>(packet.data + 8));
|
||||
if (packet_ssrc != ssrc_filter)
|
||||
continue;
|
||||
}
|
||||
|
||||
rtp_writer->WritePacket(&packet);
|
||||
rtp_counter++;
|
||||
}
|
||||
if (FLAG_rtcp && parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::RTCP_EVENT) {
|
||||
webrtc::test::RtpPacket packet;
|
||||
webrtc::PacketDirection direction;
|
||||
parsed_stream.GetRtcpPacket(i, &direction, packet.data, &packet.length);
|
||||
// For RTCP packets the original_length should be set to 0 in the
|
||||
// RTPdump format.
|
||||
packet.original_length = 0;
|
||||
packet.time_ms = parsed_stream.GetTimestamp(i) / 1000;
|
||||
|
||||
// TODO(terelius): Maybe add a flag to dump outgoing traffic instead?
|
||||
if (direction == webrtc::kOutgoingPacket)
|
||||
continue;
|
||||
|
||||
// Note that |packet_ssrc| is the sender SSRC. An RTCP message may contain
|
||||
// report blocks for many streams, thus several SSRCs and they doen't
|
||||
// necessarily have to be of the same media type.
|
||||
const uint32_t packet_ssrc = webrtc::ByteReader<uint32_t>::ReadBigEndian(
|
||||
reinterpret_cast<const uint8_t*>(packet.data + 4));
|
||||
MediaType media_type = parsed_stream.GetMediaType(packet_ssrc, direction);
|
||||
if (!FLAG_audio && media_type == MediaType::AUDIO)
|
||||
continue;
|
||||
if (!FLAG_video && media_type == MediaType::VIDEO)
|
||||
continue;
|
||||
if (!FLAG_data && media_type == MediaType::DATA)
|
||||
continue;
|
||||
if (strlen(FLAG_ssrc) > 0) {
|
||||
if (packet_ssrc != ssrc_filter)
|
||||
continue;
|
||||
}
|
||||
|
||||
rtp_writer->WritePacket(&packet);
|
||||
rtcp_counter++;
|
||||
}
|
||||
}
|
||||
std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "")
|
||||
<< " RTP packets and " << rtcp_counter << " RTCP packets to the "
|
||||
<< "output file." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
257
logging/rtc_event_log/rtc_event_log2stats.cc
Normal file
257
logging/rtc_event_log/rtc_event_log2stats.cc
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/flags.h"
|
||||
#include "webrtc/rtc_base/ignore_wundef.h"
|
||||
#include "webrtc/rtc_base/logging.h"
|
||||
|
||||
// Files generated at build-time by the protobuf compiler.
|
||||
RTC_PUSH_IGNORING_WUNDEF()
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#else
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#endif
|
||||
RTC_POP_IGNORING_WUNDEF()
|
||||
|
||||
namespace {
|
||||
|
||||
DEFINE_bool(help, false, "Prints this message.");
|
||||
|
||||
struct Stats {
|
||||
int count = 0;
|
||||
size_t total_size = 0;
|
||||
};
|
||||
|
||||
// We are duplicating some parts of the parser here because we want to get
|
||||
// access to raw protobuf events.
|
||||
std::pair<uint64_t, bool> ParseVarInt(std::istream& stream) {
|
||||
uint64_t varint = 0;
|
||||
for (size_t bytes_read = 0; bytes_read < 10; ++bytes_read) {
|
||||
// The most significant bit of each byte is 0 if it is the last byte in
|
||||
// the varint and 1 otherwise. Thus, we take the 7 least significant bits
|
||||
// of each byte and shift them 7 bits for each byte read previously to get
|
||||
// the (unsigned) integer.
|
||||
int byte = stream.get();
|
||||
if (stream.eof()) {
|
||||
return std::make_pair(varint, false);
|
||||
}
|
||||
RTC_DCHECK_GE(byte, 0);
|
||||
RTC_DCHECK_LE(byte, 255);
|
||||
varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * bytes_read);
|
||||
if ((byte & 0x80) == 0) {
|
||||
return std::make_pair(varint, true);
|
||||
}
|
||||
}
|
||||
return std::make_pair(varint, false);
|
||||
}
|
||||
|
||||
bool ParseEvents(const std::string& filename,
|
||||
std::vector<webrtc::rtclog::Event>* events) {
|
||||
std::ifstream stream(filename, std::ios_base::in | std::ios_base::binary);
|
||||
if (!stream.good() || !stream.is_open()) {
|
||||
LOG(LS_WARNING) << "Could not open file for reading.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t kMaxEventSize = (1u << 16) - 1;
|
||||
std::vector<char> tmp_buffer(kMaxEventSize);
|
||||
uint64_t tag;
|
||||
uint64_t message_length;
|
||||
bool success;
|
||||
|
||||
RTC_DCHECK(stream.good());
|
||||
|
||||
while (1) {
|
||||
// Check whether we have reached end of file.
|
||||
stream.peek();
|
||||
if (stream.eof()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the next message tag. The tag number is defined as
|
||||
// (fieldnumber << 3) | wire_type. In our case, the field number is
|
||||
// supposed to be 1 and the wire type for an length-delimited field is 2.
|
||||
const uint64_t kExpectedTag = (1 << 3) | 2;
|
||||
std::tie(tag, success) = ParseVarInt(stream);
|
||||
if (!success) {
|
||||
LOG(LS_WARNING) << "Missing field tag from beginning of protobuf event.";
|
||||
return false;
|
||||
} else if (tag != kExpectedTag) {
|
||||
LOG(LS_WARNING) << "Unexpected field tag at beginning of protobuf event.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the length field.
|
||||
std::tie(message_length, success) = ParseVarInt(stream);
|
||||
if (!success) {
|
||||
LOG(LS_WARNING) << "Missing message length after protobuf field tag.";
|
||||
return false;
|
||||
} else if (message_length > kMaxEventSize) {
|
||||
LOG(LS_WARNING) << "Protobuf message length is too large.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the next protobuf event to a temporary char buffer.
|
||||
stream.read(tmp_buffer.data(), message_length);
|
||||
if (stream.gcount() != static_cast<int>(message_length)) {
|
||||
LOG(LS_WARNING) << "Failed to read protobuf message from file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the protobuf event from the buffer.
|
||||
webrtc::rtclog::Event event;
|
||||
if (!event.ParseFromArray(tmp_buffer.data(), message_length)) {
|
||||
LOG(LS_WARNING) << "Failed to parse protobuf message.";
|
||||
return false;
|
||||
}
|
||||
events->push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(terelius): Should this be placed in some utility file instead?
|
||||
std::string EventTypeToString(webrtc::rtclog::Event::EventType event_type) {
|
||||
switch (event_type) {
|
||||
case webrtc::rtclog::Event::UNKNOWN_EVENT:
|
||||
return "UNKNOWN_EVENT";
|
||||
case webrtc::rtclog::Event::LOG_START:
|
||||
return "LOG_START";
|
||||
case webrtc::rtclog::Event::LOG_END:
|
||||
return "LOG_END";
|
||||
case webrtc::rtclog::Event::RTP_EVENT:
|
||||
return "RTP_EVENT";
|
||||
case webrtc::rtclog::Event::RTCP_EVENT:
|
||||
return "RTCP_EVENT";
|
||||
case webrtc::rtclog::Event::AUDIO_PLAYOUT_EVENT:
|
||||
return "AUDIO_PLAYOUT_EVENT";
|
||||
case webrtc::rtclog::Event::LOSS_BASED_BWE_UPDATE:
|
||||
return "LOSS_BASED_BWE_UPDATE";
|
||||
case webrtc::rtclog::Event::DELAY_BASED_BWE_UPDATE:
|
||||
return "DELAY_BASED_BWE_UPDATE";
|
||||
case webrtc::rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT:
|
||||
return "VIDEO_RECV_CONFIG";
|
||||
case webrtc::rtclog::Event::VIDEO_SENDER_CONFIG_EVENT:
|
||||
return "VIDEO_SEND_CONFIG";
|
||||
case webrtc::rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT:
|
||||
return "AUDIO_RECV_CONFIG";
|
||||
case webrtc::rtclog::Event::AUDIO_SENDER_CONFIG_EVENT:
|
||||
return "AUDIO_SEND_CONFIG";
|
||||
case webrtc::rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT:
|
||||
return "AUDIO_NETWORK_ADAPTATION";
|
||||
case webrtc::rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT:
|
||||
return "BWE_PROBE_CREATED";
|
||||
case webrtc::rtclog::Event::BWE_PROBE_RESULT_EVENT:
|
||||
return "BWE_PROBE_RESULT";
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return "UNKNOWN_EVENT";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// This utility will print basic information about each packet to stdout.
|
||||
// Note that parser will assert if the protobuf event is missing some required
|
||||
// fields and we attempt to access them. We don't handle this at the moment.
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage =
|
||||
"Tool for file usage statistics from an RtcEventLog.\n"
|
||||
"Run " +
|
||||
program_name +
|
||||
" --help for usage.\n"
|
||||
"Example usage:\n" +
|
||||
program_name + " input.rel\n";
|
||||
if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) ||
|
||||
FLAG_help || argc != 2) {
|
||||
std::cout << usage;
|
||||
if (FLAG_help) {
|
||||
rtc::FlagList::Print(nullptr, false);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
std::string file_name = argv[1];
|
||||
|
||||
std::vector<webrtc::rtclog::Event> events;
|
||||
if (!ParseEvents(file_name, &events)) {
|
||||
LOG(LS_ERROR) << "Failed to parse event log.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get file size
|
||||
FILE* file = fopen(file_name.c_str(), "rb");
|
||||
fseek(file, 0L, SEEK_END);
|
||||
int64_t file_size = ftell(file);
|
||||
fclose(file);
|
||||
|
||||
// We are deliberately using low level protobuf functions to get the stats
|
||||
// since the convenience functions in the parser would CHECK that the events
|
||||
// are well formed.
|
||||
std::map<webrtc::rtclog::Event::EventType, Stats> stats;
|
||||
int malformed_events = 0;
|
||||
size_t malformed_event_size = 0;
|
||||
size_t accumulated_event_size = 0;
|
||||
for (const webrtc::rtclog::Event& event : events) {
|
||||
size_t serialized_size = event.ByteSize();
|
||||
// When the event is written on the disk, it is part of an EventStream
|
||||
// object. The event stream will prepend a 1 byte field number/wire type,
|
||||
// and a varint encoding (base 128) of the event length.
|
||||
serialized_size =
|
||||
1 + (1 + (serialized_size > 127) + (serialized_size > 16383)) +
|
||||
serialized_size;
|
||||
|
||||
if (event.has_type() && event.has_timestamp_us()) {
|
||||
stats[event.type()].count++;
|
||||
stats[event.type()].total_size += serialized_size;
|
||||
} else {
|
||||
// The event is missing the type or the timestamp field.
|
||||
malformed_events++;
|
||||
malformed_event_size += serialized_size;
|
||||
}
|
||||
accumulated_event_size += serialized_size;
|
||||
}
|
||||
|
||||
printf("Type \tCount\tTotal size\tAverage size\tPercent\n");
|
||||
printf(
|
||||
"-----------------------------------------------------------------------"
|
||||
"\n");
|
||||
for (const auto it : stats) {
|
||||
printf("%-22s\t%5d\t%10zu\t%12.2lf\t%7.2lf\n",
|
||||
EventTypeToString(it.first).c_str(), it.second.count,
|
||||
it.second.total_size,
|
||||
static_cast<double>(it.second.total_size) / it.second.count,
|
||||
static_cast<double>(it.second.total_size) / file_size * 100);
|
||||
}
|
||||
if (malformed_events != 0) {
|
||||
printf("%-22s\t%5d\t%10zu\t%12.2lf\t%7.2lf\n", "MALFORMED",
|
||||
malformed_events, malformed_event_size,
|
||||
static_cast<double>(malformed_event_size) / malformed_events,
|
||||
static_cast<double>(malformed_event_size) / file_size * 100);
|
||||
}
|
||||
if (file_size - accumulated_event_size != 0) {
|
||||
printf("WARNING: %" PRId64 " bytes not accounted for\n",
|
||||
file_size - accumulated_event_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
575
logging/rtc_event_log/rtc_event_log2text.cc
Normal file
575
logging/rtc_event_log/rtc_event_log2text.cc
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 <string.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility> // pair
|
||||
|
||||
#include "webrtc/call/video_config.h"
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_parser.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/fir.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/flags.h"
|
||||
|
||||
namespace {
|
||||
|
||||
DEFINE_bool(config, true, "Use --noconfig to exclude stream configurations.");
|
||||
DEFINE_bool(incoming, true, "Use --noincoming to exclude incoming packets.");
|
||||
DEFINE_bool(outgoing, true, "Use --nooutgoing to exclude packets.");
|
||||
// TODO(terelius): Note that the media type doesn't work with outgoing packets.
|
||||
DEFINE_bool(audio, true, "Use --noaudio to exclude audio packets.");
|
||||
// TODO(terelius): Note that the media type doesn't work with outgoing packets.
|
||||
DEFINE_bool(video, true, "Use --novideo to exclude video packets.");
|
||||
// TODO(terelius): Note that the media type doesn't work with outgoing packets.
|
||||
DEFINE_bool(data, true, "Use --nodata to exclude data packets.");
|
||||
DEFINE_bool(rtp, true, "Use --nortp to exclude RTP packets.");
|
||||
DEFINE_bool(rtcp, true, "Use --nortcp to exclude RTCP packets.");
|
||||
// TODO(terelius): Allow a list of SSRCs.
|
||||
DEFINE_string(ssrc,
|
||||
"",
|
||||
"Print only packets with this SSRC (decimal or hex, the latter "
|
||||
"starting with 0x).");
|
||||
DEFINE_bool(help, false, "Prints this message.");
|
||||
|
||||
using MediaType = webrtc::ParsedRtcEventLog::MediaType;
|
||||
|
||||
static uint32_t filtered_ssrc = 0;
|
||||
|
||||
// Parses the input string for a valid SSRC. If a valid SSRC is found, it is
|
||||
// written to the static global variable |filtered_ssrc|, and true is returned.
|
||||
// Otherwise, false is returned.
|
||||
// The empty string must be validated as true, because it is the default value
|
||||
// of the command-line flag. In this case, no value is written to the output
|
||||
// variable.
|
||||
bool ParseSsrc(std::string str) {
|
||||
// If the input string starts with 0x or 0X it indicates a hexadecimal number.
|
||||
auto read_mode = std::dec;
|
||||
if (str.size() > 2 &&
|
||||
(str.substr(0, 2) == "0x" || str.substr(0, 2) == "0X")) {
|
||||
read_mode = std::hex;
|
||||
str = str.substr(2);
|
||||
}
|
||||
std::stringstream ss(str);
|
||||
ss >> read_mode >> filtered_ssrc;
|
||||
return str.empty() || (!ss.fail() && ss.eof());
|
||||
}
|
||||
|
||||
bool ExcludePacket(webrtc::PacketDirection direction,
|
||||
MediaType media_type,
|
||||
uint32_t packet_ssrc) {
|
||||
if (!FLAG_outgoing && direction == webrtc::kOutgoingPacket)
|
||||
return true;
|
||||
if (!FLAG_incoming && direction == webrtc::kIncomingPacket)
|
||||
return true;
|
||||
if (!FLAG_audio && media_type == MediaType::AUDIO)
|
||||
return true;
|
||||
if (!FLAG_video && media_type == MediaType::VIDEO)
|
||||
return true;
|
||||
if (!FLAG_data && media_type == MediaType::DATA)
|
||||
return true;
|
||||
if (strlen(FLAG_ssrc) > 0 && packet_ssrc != filtered_ssrc)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* StreamInfo(webrtc::PacketDirection direction,
|
||||
MediaType media_type) {
|
||||
if (direction == webrtc::kOutgoingPacket) {
|
||||
if (media_type == MediaType::AUDIO)
|
||||
return "(out,audio)";
|
||||
else if (media_type == MediaType::VIDEO)
|
||||
return "(out,video)";
|
||||
else if (media_type == MediaType::DATA)
|
||||
return "(out,data)";
|
||||
else
|
||||
return "(out)";
|
||||
}
|
||||
if (direction == webrtc::kIncomingPacket) {
|
||||
if (media_type == MediaType::AUDIO)
|
||||
return "(in,audio)";
|
||||
else if (media_type == MediaType::VIDEO)
|
||||
return "(in,video)";
|
||||
else if (media_type == MediaType::DATA)
|
||||
return "(in,data)";
|
||||
else
|
||||
return "(in)";
|
||||
}
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
// Return default values for header extensions, to use on streams without stored
|
||||
// mapping data. Currently this only applies to audio streams, since the mapping
|
||||
// is not stored in the event log.
|
||||
// TODO(ivoc): Remove this once this mapping is stored in the event log for
|
||||
// audio streams. Tracking bug: webrtc:6399
|
||||
webrtc::RtpHeaderExtensionMap GetDefaultHeaderExtensionMap() {
|
||||
webrtc::RtpHeaderExtensionMap default_map;
|
||||
default_map.Register<webrtc::AudioLevel>(
|
||||
webrtc::RtpExtension::kAudioLevelDefaultId);
|
||||
default_map.Register<webrtc::TransmissionOffset>(
|
||||
webrtc::RtpExtension::kTimestampOffsetDefaultId);
|
||||
default_map.Register<webrtc::AbsoluteSendTime>(
|
||||
webrtc::RtpExtension::kAbsSendTimeDefaultId);
|
||||
default_map.Register<webrtc::VideoOrientation>(
|
||||
webrtc::RtpExtension::kVideoRotationDefaultId);
|
||||
default_map.Register<webrtc::VideoContentTypeExtension>(
|
||||
webrtc::RtpExtension::kVideoContentTypeDefaultId);
|
||||
default_map.Register<webrtc::VideoTimingExtension>(
|
||||
webrtc::RtpExtension::kVideoTimingDefaultId);
|
||||
default_map.Register<webrtc::TransportSequenceNumber>(
|
||||
webrtc::RtpExtension::kTransportSequenceNumberDefaultId);
|
||||
default_map.Register<webrtc::PlayoutDelayLimits>(
|
||||
webrtc::RtpExtension::kPlayoutDelayDefaultId);
|
||||
return default_map;
|
||||
}
|
||||
|
||||
void PrintSenderReport(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
webrtc::rtcp::SenderReport sr;
|
||||
if (!sr.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(sr.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, sr.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_SR" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << sr.sender_ssrc()
|
||||
<< "\ttimestamp=" << sr.rtp_timestamp() << std::endl;
|
||||
}
|
||||
|
||||
void PrintReceiverReport(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
webrtc::rtcp::ReceiverReport rr;
|
||||
if (!rr.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(rr.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, rr.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_RR" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << rr.sender_ssrc() << std::endl;
|
||||
}
|
||||
|
||||
void PrintXr(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
webrtc::rtcp::ExtendedReports xr;
|
||||
if (!xr.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(xr.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, xr.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_XR" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << xr.sender_ssrc() << std::endl;
|
||||
}
|
||||
|
||||
void PrintSdes(const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_SDES" << StreamInfo(direction, MediaType::ANY)
|
||||
<< std::endl;
|
||||
RTC_NOTREACHED() << "SDES should have been redacted when writing the log";
|
||||
}
|
||||
|
||||
void PrintBye(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
webrtc::rtcp::Bye bye;
|
||||
if (!bye.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(bye.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, bye.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_BYE" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << bye.sender_ssrc() << std::endl;
|
||||
}
|
||||
|
||||
void PrintRtpFeedback(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
switch (rtcp_block.fmt()) {
|
||||
case webrtc::rtcp::Nack::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Nack nack;
|
||||
if (!nack.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(nack.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, nack.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_NACK" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << nack.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::Tmmbr::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Tmmbr tmmbr;
|
||||
if (!tmmbr.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(tmmbr.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, tmmbr.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_TMMBR" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << tmmbr.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::Tmmbn::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Tmmbn tmmbn;
|
||||
if (!tmmbn.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(tmmbn.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, tmmbn.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_TMMBN" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << tmmbn.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::RapidResyncRequest::kFeedbackMessageType: {
|
||||
webrtc::rtcp::RapidResyncRequest sr_req;
|
||||
if (!sr_req.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(sr_req.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, sr_req.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_SRREQ" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << sr_req.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::TransportFeedback::kFeedbackMessageType: {
|
||||
webrtc::rtcp::TransportFeedback transport_feedback;
|
||||
if (!transport_feedback.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type = parsed_stream.GetMediaType(
|
||||
transport_feedback.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type,
|
||||
transport_feedback.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_NEWFB" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << transport_feedback.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPsFeedback(const webrtc::ParsedRtcEventLog& parsed_stream,
|
||||
const webrtc::rtcp::CommonHeader& rtcp_block,
|
||||
uint64_t log_timestamp,
|
||||
webrtc::PacketDirection direction) {
|
||||
switch (rtcp_block.fmt()) {
|
||||
case webrtc::rtcp::Pli::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Pli pli;
|
||||
if (!pli.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(pli.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, pli.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_PLI" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << pli.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::Fir::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Fir fir;
|
||||
if (!fir.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(fir.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, fir.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_FIR" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << fir.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
case webrtc::rtcp::Remb::kFeedbackMessageType: {
|
||||
webrtc::rtcp::Remb remb;
|
||||
if (!remb.Parse(rtcp_block))
|
||||
return;
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(remb.sender_ssrc(), direction);
|
||||
if (ExcludePacket(direction, media_type, remb.sender_ssrc()))
|
||||
return;
|
||||
std::cout << log_timestamp << "\t"
|
||||
<< "RTCP_REMB" << StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << remb.sender_ssrc() << std::endl;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// This utility will print basic information about each packet to stdout.
|
||||
// Note that parser will assert if the protobuf event is missing some required
|
||||
// fields and we attempt to access them. We don't handle this at the moment.
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage =
|
||||
"Tool for printing packet information from an RtcEventLog as text.\n"
|
||||
"Run " +
|
||||
program_name +
|
||||
" --help for usage.\n"
|
||||
"Example usage:\n" +
|
||||
program_name + " input.rel\n";
|
||||
if (rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true) ||
|
||||
FLAG_help || argc != 2) {
|
||||
std::cout << usage;
|
||||
if (FLAG_help) {
|
||||
rtc::FlagList::Print(nullptr, false);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
std::string input_file = argv[1];
|
||||
|
||||
if (strlen(FLAG_ssrc) > 0)
|
||||
RTC_CHECK(ParseSsrc(FLAG_ssrc)) << "Flag verification has failed.";
|
||||
|
||||
webrtc::RtpHeaderExtensionMap default_map = GetDefaultHeaderExtensionMap();
|
||||
|
||||
webrtc::ParsedRtcEventLog parsed_stream;
|
||||
if (!parsed_stream.ParseFile(input_file)) {
|
||||
std::cerr << "Error while parsing input file: " << input_file << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < parsed_stream.GetNumberOfEvents(); i++) {
|
||||
if (FLAG_config && FLAG_video && FLAG_incoming &&
|
||||
parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
|
||||
webrtc::rtclog::StreamConfig config =
|
||||
parsed_stream.GetVideoReceiveConfig(i);
|
||||
std::cout << parsed_stream.GetTimestamp(i) << "\tVIDEO_RECV_CONFIG"
|
||||
<< "\tssrc=" << config.remote_ssrc
|
||||
<< "\tfeedback_ssrc=" << config.local_ssrc;
|
||||
std::cout << "\textensions={";
|
||||
for (const auto& extension : config.rtp_extensions) {
|
||||
std::cout << extension.ToString() << ",";
|
||||
}
|
||||
std::cout << "}";
|
||||
std::cout << "\tcodecs={";
|
||||
for (const auto& codec : config.codecs) {
|
||||
std::cout << "{name: " << codec.payload_name
|
||||
<< ", payload_type: " << codec.payload_type
|
||||
<< ", rtx_payload_type: " << codec.rtx_payload_type << "}";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
if (FLAG_config && FLAG_video && FLAG_outgoing &&
|
||||
parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
|
||||
std::vector<webrtc::rtclog::StreamConfig> configs =
|
||||
parsed_stream.GetVideoSendConfig(i);
|
||||
for (const auto& config : configs) {
|
||||
std::cout << parsed_stream.GetTimestamp(i) << "\tVIDEO_SEND_CONFIG";
|
||||
std::cout << "\tssrcs=" << config.local_ssrc;
|
||||
std::cout << "\trtx_ssrcs=" << config.rtx_ssrc;
|
||||
std::cout << "\textensions={";
|
||||
for (const auto& extension : config.rtp_extensions) {
|
||||
std::cout << extension.ToString() << ",";
|
||||
}
|
||||
std::cout << "}";
|
||||
std::cout << "\tcodecs={";
|
||||
for (const auto& codec : config.codecs) {
|
||||
std::cout << "{name: " << codec.payload_name
|
||||
<< ", payload_type: " << codec.payload_type
|
||||
<< ", rtx_payload_type: " << codec.rtx_payload_type << "}";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
}
|
||||
if (FLAG_config && FLAG_audio && FLAG_incoming &&
|
||||
parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
|
||||
webrtc::rtclog::StreamConfig config =
|
||||
parsed_stream.GetAudioReceiveConfig(i);
|
||||
std::cout << parsed_stream.GetTimestamp(i) << "\tAUDIO_RECV_CONFIG"
|
||||
<< "\tssrc=" << config.remote_ssrc
|
||||
<< "\tfeedback_ssrc=" << config.local_ssrc;
|
||||
std::cout << "\textensions={";
|
||||
for (const auto& extension : config.rtp_extensions) {
|
||||
std::cout << extension.ToString() << ",";
|
||||
}
|
||||
std::cout << "}";
|
||||
std::cout << "\tcodecs={";
|
||||
for (const auto& codec : config.codecs) {
|
||||
std::cout << "{name: " << codec.payload_name
|
||||
<< ", payload_type: " << codec.payload_type
|
||||
<< ", rtx_payload_type: " << codec.rtx_payload_type << "}";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
if (FLAG_config && FLAG_audio && FLAG_outgoing &&
|
||||
parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
|
||||
webrtc::rtclog::StreamConfig config = parsed_stream.GetAudioSendConfig(i);
|
||||
std::cout << parsed_stream.GetTimestamp(i) << "\tAUDIO_SEND_CONFIG"
|
||||
<< "\tssrc=" << config.local_ssrc;
|
||||
std::cout << "\textensions={";
|
||||
for (const auto& extension : config.rtp_extensions) {
|
||||
std::cout << extension.ToString() << ",";
|
||||
}
|
||||
std::cout << "}";
|
||||
std::cout << "\tcodecs={";
|
||||
for (const auto& codec : config.codecs) {
|
||||
std::cout << "{name: " << codec.payload_name
|
||||
<< ", payload_type: " << codec.payload_type
|
||||
<< ", rtx_payload_type: " << codec.rtx_payload_type << "}";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
if (FLAG_rtp &&
|
||||
parsed_stream.GetEventType(i) == webrtc::ParsedRtcEventLog::RTP_EVENT) {
|
||||
size_t header_length;
|
||||
size_t total_length;
|
||||
uint8_t header[IP_PACKET_SIZE];
|
||||
webrtc::PacketDirection direction;
|
||||
webrtc::RtpHeaderExtensionMap* extension_map = parsed_stream.GetRtpHeader(
|
||||
i, &direction, header, &header_length, &total_length);
|
||||
|
||||
if (extension_map == nullptr)
|
||||
extension_map = &default_map;
|
||||
|
||||
// Parse header to get SSRC and RTP time.
|
||||
webrtc::RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
|
||||
webrtc::RTPHeader parsed_header;
|
||||
rtp_parser.Parse(&parsed_header, extension_map);
|
||||
MediaType media_type =
|
||||
parsed_stream.GetMediaType(parsed_header.ssrc, direction);
|
||||
|
||||
if (ExcludePacket(direction, media_type, parsed_header.ssrc))
|
||||
continue;
|
||||
|
||||
std::cout << parsed_stream.GetTimestamp(i) << "\tRTP"
|
||||
<< StreamInfo(direction, media_type)
|
||||
<< "\tssrc=" << parsed_header.ssrc
|
||||
<< "\ttimestamp=" << parsed_header.timestamp;
|
||||
if (parsed_header.extension.hasAbsoluteSendTime) {
|
||||
std::cout << "\tAbsSendTime="
|
||||
<< parsed_header.extension.absoluteSendTime;
|
||||
}
|
||||
if (parsed_header.extension.hasVideoContentType) {
|
||||
std::cout << "\tContentType="
|
||||
<< static_cast<int>(parsed_header.extension.videoContentType);
|
||||
}
|
||||
if (parsed_header.extension.hasVideoRotation) {
|
||||
std::cout << "\tRotation="
|
||||
<< static_cast<int>(parsed_header.extension.videoRotation);
|
||||
}
|
||||
if (parsed_header.extension.hasTransportSequenceNumber) {
|
||||
std::cout << "\tTransportSeq="
|
||||
<< parsed_header.extension.transportSequenceNumber;
|
||||
}
|
||||
if (parsed_header.extension.hasTransmissionTimeOffset) {
|
||||
std::cout << "\tTransmTimeOffset="
|
||||
<< parsed_header.extension.transmissionTimeOffset;
|
||||
}
|
||||
if (parsed_header.extension.hasAudioLevel) {
|
||||
std::cout << "\tAudioLevel=" << parsed_header.extension.audioLevel;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
if (FLAG_rtcp && parsed_stream.GetEventType(i) ==
|
||||
webrtc::ParsedRtcEventLog::RTCP_EVENT) {
|
||||
size_t length;
|
||||
uint8_t packet[IP_PACKET_SIZE];
|
||||
webrtc::PacketDirection direction;
|
||||
parsed_stream.GetRtcpPacket(i, &direction, packet, &length);
|
||||
|
||||
webrtc::rtcp::CommonHeader rtcp_block;
|
||||
const uint8_t* packet_end = packet + length;
|
||||
for (const uint8_t* next_block = packet; next_block != packet_end;
|
||||
next_block = rtcp_block.NextPacket()) {
|
||||
ptrdiff_t remaining_blocks_size = packet_end - next_block;
|
||||
RTC_DCHECK_GT(remaining_blocks_size, 0);
|
||||
if (!rtcp_block.Parse(next_block, remaining_blocks_size)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t log_timestamp = parsed_stream.GetTimestamp(i);
|
||||
switch (rtcp_block.type()) {
|
||||
case webrtc::rtcp::SenderReport::kPacketType:
|
||||
PrintSenderReport(parsed_stream, rtcp_block, log_timestamp,
|
||||
direction);
|
||||
break;
|
||||
case webrtc::rtcp::ReceiverReport::kPacketType:
|
||||
PrintReceiverReport(parsed_stream, rtcp_block, log_timestamp,
|
||||
direction);
|
||||
break;
|
||||
case webrtc::rtcp::Sdes::kPacketType:
|
||||
PrintSdes(rtcp_block, log_timestamp, direction);
|
||||
break;
|
||||
case webrtc::rtcp::ExtendedReports::kPacketType:
|
||||
PrintXr(parsed_stream, rtcp_block, log_timestamp, direction);
|
||||
break;
|
||||
case webrtc::rtcp::Bye::kPacketType:
|
||||
PrintBye(parsed_stream, rtcp_block, log_timestamp, direction);
|
||||
break;
|
||||
case webrtc::rtcp::Rtpfb::kPacketType:
|
||||
PrintRtpFeedback(parsed_stream, rtcp_block, log_timestamp,
|
||||
direction);
|
||||
break;
|
||||
case webrtc::rtcp::Psfb::kPacketType:
|
||||
PrintPsFeedback(parsed_stream, rtcp_block, log_timestamp,
|
||||
direction);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
25
logging/rtc_event_log/rtc_event_log_factory.cc
Normal file
25
logging/rtc_event_log/rtc_event_log_factory.cc
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 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 "webrtc/logging/rtc_event_log/rtc_event_log_factory.h"
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::unique_ptr<RtcEventLog> RtcEventLogFactory::CreateRtcEventLog() {
|
||||
return RtcEventLog::Create();
|
||||
}
|
||||
|
||||
std::unique_ptr<RtcEventLogFactoryInterface> CreateRtcEventLogFactory() {
|
||||
return std::unique_ptr<RtcEventLogFactoryInterface>(new RtcEventLogFactory());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
30
logging/rtc_event_log/rtc_event_log_factory.h
Normal file
30
logging/rtc_event_log/rtc_event_log_factory.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_factory_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtcEventLogFactory : public RtcEventLogFactoryInterface {
|
||||
public:
|
||||
~RtcEventLogFactory() override {}
|
||||
|
||||
std::unique_ptr<RtcEventLog> CreateRtcEventLog() override;
|
||||
};
|
||||
|
||||
std::unique_ptr<RtcEventLogFactoryInterface> CreateRtcEventLogFactory();
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_H_
|
||||
34
logging/rtc_event_log/rtc_event_log_factory_interface.h
Normal file
34
logging/rtc_event_log/rtc_event_log_factory_interface.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_INTERFACE_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This interface exists to allow webrtc to be optionally built without
|
||||
// RtcEventLog support. A PeerConnectionFactory is constructed with an
|
||||
// RtcEventLogFactoryInterface, which may or may not be null.
|
||||
class RtcEventLogFactoryInterface {
|
||||
public:
|
||||
virtual ~RtcEventLogFactoryInterface() {}
|
||||
|
||||
virtual std::unique_ptr<RtcEventLog> CreateRtcEventLog() = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<RtcEventLogFactoryInterface> CreateRtcEventLogFactory();
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_FACTORY_INTERFACE_H_
|
||||
657
logging/rtc_event_log/rtc_event_log_parser.cc
Normal file
657
logging/rtc_event_log/rtc_event_log_parser.cc
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 "webrtc/logging/rtc_event_log/rtc_event_log_parser.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/logging.h"
|
||||
#include "webrtc/rtc_base/protobuf_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) {
|
||||
switch (rtcp_mode) {
|
||||
case rtclog::VideoReceiveConfig::RTCP_COMPOUND:
|
||||
return RtcpMode::kCompound;
|
||||
case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE:
|
||||
return RtcpMode::kReducedSize;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return RtcpMode::kOff;
|
||||
}
|
||||
|
||||
ParsedRtcEventLog::EventType GetRuntimeEventType(
|
||||
rtclog::Event::EventType event_type) {
|
||||
switch (event_type) {
|
||||
case rtclog::Event::UNKNOWN_EVENT:
|
||||
return ParsedRtcEventLog::EventType::UNKNOWN_EVENT;
|
||||
case rtclog::Event::LOG_START:
|
||||
return ParsedRtcEventLog::EventType::LOG_START;
|
||||
case rtclog::Event::LOG_END:
|
||||
return ParsedRtcEventLog::EventType::LOG_END;
|
||||
case rtclog::Event::RTP_EVENT:
|
||||
return ParsedRtcEventLog::EventType::RTP_EVENT;
|
||||
case rtclog::Event::RTCP_EVENT:
|
||||
return ParsedRtcEventLog::EventType::RTCP_EVENT;
|
||||
case rtclog::Event::AUDIO_PLAYOUT_EVENT:
|
||||
return ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT;
|
||||
case rtclog::Event::LOSS_BASED_BWE_UPDATE:
|
||||
return ParsedRtcEventLog::EventType::LOSS_BASED_BWE_UPDATE;
|
||||
case rtclog::Event::DELAY_BASED_BWE_UPDATE:
|
||||
return ParsedRtcEventLog::EventType::DELAY_BASED_BWE_UPDATE;
|
||||
case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT:
|
||||
return ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT;
|
||||
case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT:
|
||||
return ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT;
|
||||
case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT:
|
||||
return ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT;
|
||||
case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT:
|
||||
return ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT;
|
||||
case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT:
|
||||
return ParsedRtcEventLog::EventType::AUDIO_NETWORK_ADAPTATION_EVENT;
|
||||
case rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT:
|
||||
return ParsedRtcEventLog::EventType::BWE_PROBE_CLUSTER_CREATED_EVENT;
|
||||
case rtclog::Event::BWE_PROBE_RESULT_EVENT:
|
||||
return ParsedRtcEventLog::EventType::BWE_PROBE_RESULT_EVENT;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return ParsedRtcEventLog::EventType::UNKNOWN_EVENT;
|
||||
}
|
||||
|
||||
BandwidthUsage GetRuntimeDetectorState(
|
||||
rtclog::DelayBasedBweUpdate::DetectorState detector_state) {
|
||||
switch (detector_state) {
|
||||
case rtclog::DelayBasedBweUpdate::BWE_NORMAL:
|
||||
return BandwidthUsage::kBwNormal;
|
||||
case rtclog::DelayBasedBweUpdate::BWE_UNDERUSING:
|
||||
return BandwidthUsage::kBwUnderusing;
|
||||
case rtclog::DelayBasedBweUpdate::BWE_OVERUSING:
|
||||
return BandwidthUsage::kBwOverusing;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return BandwidthUsage::kBwNormal;
|
||||
}
|
||||
|
||||
std::pair<uint64_t, bool> ParseVarInt(std::istream& stream) {
|
||||
uint64_t varint = 0;
|
||||
for (size_t bytes_read = 0; bytes_read < 10; ++bytes_read) {
|
||||
// The most significant bit of each byte is 0 if it is the last byte in
|
||||
// the varint and 1 otherwise. Thus, we take the 7 least significant bits
|
||||
// of each byte and shift them 7 bits for each byte read previously to get
|
||||
// the (unsigned) integer.
|
||||
int byte = stream.get();
|
||||
if (stream.eof()) {
|
||||
return std::make_pair(varint, false);
|
||||
}
|
||||
RTC_DCHECK_GE(byte, 0);
|
||||
RTC_DCHECK_LE(byte, 255);
|
||||
varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * bytes_read);
|
||||
if ((byte & 0x80) == 0) {
|
||||
return std::make_pair(varint, true);
|
||||
}
|
||||
}
|
||||
return std::make_pair(varint, false);
|
||||
}
|
||||
|
||||
void GetHeaderExtensions(
|
||||
std::vector<RtpExtension>* header_extensions,
|
||||
const RepeatedPtrField<rtclog::RtpHeaderExtension>&
|
||||
proto_header_extensions) {
|
||||
header_extensions->clear();
|
||||
for (auto& p : proto_header_extensions) {
|
||||
RTC_CHECK(p.has_name());
|
||||
RTC_CHECK(p.has_id());
|
||||
const std::string& name = p.name();
|
||||
int id = p.id();
|
||||
header_extensions->push_back(RtpExtension(name, id));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ParsedRtcEventLog::ParseFile(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios_base::in | std::ios_base::binary);
|
||||
if (!file.good() || !file.is_open()) {
|
||||
LOG(LS_WARNING) << "Could not open file for reading.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return ParseStream(file);
|
||||
}
|
||||
|
||||
bool ParsedRtcEventLog::ParseString(const std::string& s) {
|
||||
std::istringstream stream(s, std::ios_base::in | std::ios_base::binary);
|
||||
return ParseStream(stream);
|
||||
}
|
||||
|
||||
bool ParsedRtcEventLog::ParseStream(std::istream& stream) {
|
||||
events_.clear();
|
||||
const size_t kMaxEventSize = (1u << 16) - 1;
|
||||
std::vector<char> tmp_buffer(kMaxEventSize);
|
||||
uint64_t tag;
|
||||
uint64_t message_length;
|
||||
bool success;
|
||||
|
||||
RTC_DCHECK(stream.good());
|
||||
|
||||
while (1) {
|
||||
// Check whether we have reached end of file.
|
||||
stream.peek();
|
||||
if (stream.eof()) {
|
||||
// Process all extensions maps for faster look-up later.
|
||||
for (auto& event_stream : streams_) {
|
||||
rtp_extensions_maps_[StreamId(event_stream.ssrc,
|
||||
event_stream.direction)] =
|
||||
&event_stream.rtp_extensions_map;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the next message tag. The tag number is defined as
|
||||
// (fieldnumber << 3) | wire_type. In our case, the field number is
|
||||
// supposed to be 1 and the wire type for an
|
||||
// length-delimited field is 2.
|
||||
const uint64_t kExpectedTag = (1 << 3) | 2;
|
||||
std::tie(tag, success) = ParseVarInt(stream);
|
||||
if (!success) {
|
||||
LOG(LS_WARNING) << "Missing field tag from beginning of protobuf event.";
|
||||
return false;
|
||||
} else if (tag != kExpectedTag) {
|
||||
LOG(LS_WARNING) << "Unexpected field tag at beginning of protobuf event.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the length field.
|
||||
std::tie(message_length, success) = ParseVarInt(stream);
|
||||
if (!success) {
|
||||
LOG(LS_WARNING) << "Missing message length after protobuf field tag.";
|
||||
return false;
|
||||
} else if (message_length > kMaxEventSize) {
|
||||
LOG(LS_WARNING) << "Protobuf message length is too large.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the next protobuf event to a temporary char buffer.
|
||||
stream.read(tmp_buffer.data(), message_length);
|
||||
if (stream.gcount() != static_cast<int>(message_length)) {
|
||||
LOG(LS_WARNING) << "Failed to read protobuf message from file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the protobuf event from the buffer.
|
||||
rtclog::Event event;
|
||||
if (!event.ParseFromArray(tmp_buffer.data(), message_length)) {
|
||||
LOG(LS_WARNING) << "Failed to parse protobuf message.";
|
||||
return false;
|
||||
}
|
||||
|
||||
EventType type = GetRuntimeEventType(event.type());
|
||||
switch (type) {
|
||||
case VIDEO_RECEIVER_CONFIG_EVENT: {
|
||||
rtclog::StreamConfig config = GetVideoReceiveConfig(event);
|
||||
streams_.emplace_back(config.remote_ssrc, MediaType::VIDEO,
|
||||
kIncomingPacket,
|
||||
RtpHeaderExtensionMap(config.rtp_extensions));
|
||||
streams_.emplace_back(config.local_ssrc, MediaType::VIDEO,
|
||||
kOutgoingPacket,
|
||||
RtpHeaderExtensionMap(config.rtp_extensions));
|
||||
break;
|
||||
}
|
||||
case VIDEO_SENDER_CONFIG_EVENT: {
|
||||
std::vector<rtclog::StreamConfig> configs = GetVideoSendConfig(event);
|
||||
for (size_t i = 0; i < configs.size(); i++) {
|
||||
streams_.emplace_back(
|
||||
configs[i].local_ssrc, MediaType::VIDEO, kOutgoingPacket,
|
||||
RtpHeaderExtensionMap(configs[i].rtp_extensions));
|
||||
|
||||
streams_.emplace_back(
|
||||
configs[i].rtx_ssrc, MediaType::VIDEO, kOutgoingPacket,
|
||||
RtpHeaderExtensionMap(configs[i].rtp_extensions));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_RECEIVER_CONFIG_EVENT: {
|
||||
rtclog::StreamConfig config = GetAudioReceiveConfig(event);
|
||||
streams_.emplace_back(config.remote_ssrc, MediaType::AUDIO,
|
||||
kIncomingPacket,
|
||||
RtpHeaderExtensionMap(config.rtp_extensions));
|
||||
streams_.emplace_back(config.local_ssrc, MediaType::AUDIO,
|
||||
kOutgoingPacket,
|
||||
RtpHeaderExtensionMap(config.rtp_extensions));
|
||||
break;
|
||||
}
|
||||
case AUDIO_SENDER_CONFIG_EVENT: {
|
||||
rtclog::StreamConfig config = GetAudioSendConfig(event);
|
||||
streams_.emplace_back(config.local_ssrc, MediaType::AUDIO,
|
||||
kOutgoingPacket,
|
||||
RtpHeaderExtensionMap(config.rtp_extensions));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
events_.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ParsedRtcEventLog::GetNumberOfEvents() const {
|
||||
return events_.size();
|
||||
}
|
||||
|
||||
int64_t ParsedRtcEventLog::GetTimestamp(size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_timestamp_us());
|
||||
return event.timestamp_us();
|
||||
}
|
||||
|
||||
ParsedRtcEventLog::EventType ParsedRtcEventLog::GetEventType(
|
||||
size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
return GetRuntimeEventType(event.type());
|
||||
}
|
||||
|
||||
// The header must have space for at least IP_PACKET_SIZE bytes.
|
||||
webrtc::RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeader(
|
||||
size_t index,
|
||||
PacketDirection* incoming,
|
||||
uint8_t* header,
|
||||
size_t* header_length,
|
||||
size_t* total_length) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::RTP_EVENT);
|
||||
RTC_CHECK(event.has_rtp_packet());
|
||||
const rtclog::RtpPacket& rtp_packet = event.rtp_packet();
|
||||
// Get direction of packet.
|
||||
RTC_CHECK(rtp_packet.has_incoming());
|
||||
if (incoming != nullptr) {
|
||||
*incoming = rtp_packet.incoming() ? kIncomingPacket : kOutgoingPacket;
|
||||
}
|
||||
// Get packet length.
|
||||
RTC_CHECK(rtp_packet.has_packet_length());
|
||||
if (total_length != nullptr) {
|
||||
*total_length = rtp_packet.packet_length();
|
||||
}
|
||||
// Get header length.
|
||||
RTC_CHECK(rtp_packet.has_header());
|
||||
if (header_length != nullptr) {
|
||||
*header_length = rtp_packet.header().size();
|
||||
}
|
||||
// Get header contents.
|
||||
if (header != nullptr) {
|
||||
const size_t kMinRtpHeaderSize = 12;
|
||||
RTC_CHECK_GE(rtp_packet.header().size(), kMinRtpHeaderSize);
|
||||
RTC_CHECK_LE(rtp_packet.header().size(),
|
||||
static_cast<size_t>(IP_PACKET_SIZE));
|
||||
memcpy(header, rtp_packet.header().data(), rtp_packet.header().size());
|
||||
uint32_t ssrc = ByteReader<uint32_t>::ReadBigEndian(header + 8);
|
||||
StreamId stream_id(
|
||||
ssrc, rtp_packet.incoming() ? kIncomingPacket : kOutgoingPacket);
|
||||
auto it = rtp_extensions_maps_.find(stream_id);
|
||||
if (it != rtp_extensions_maps_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The packet must have space for at least IP_PACKET_SIZE bytes.
|
||||
void ParsedRtcEventLog::GetRtcpPacket(size_t index,
|
||||
PacketDirection* incoming,
|
||||
uint8_t* packet,
|
||||
size_t* length) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::RTCP_EVENT);
|
||||
RTC_CHECK(event.has_rtcp_packet());
|
||||
const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet();
|
||||
// Get direction of packet.
|
||||
RTC_CHECK(rtcp_packet.has_incoming());
|
||||
if (incoming != nullptr) {
|
||||
*incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket;
|
||||
}
|
||||
// Get packet length.
|
||||
RTC_CHECK(rtcp_packet.has_packet_data());
|
||||
if (length != nullptr) {
|
||||
*length = rtcp_packet.packet_data().size();
|
||||
}
|
||||
// Get packet contents.
|
||||
if (packet != nullptr) {
|
||||
RTC_CHECK_LE(rtcp_packet.packet_data().size(),
|
||||
static_cast<unsigned>(IP_PACKET_SIZE));
|
||||
memcpy(packet, rtcp_packet.packet_data().data(),
|
||||
rtcp_packet.packet_data().size());
|
||||
}
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetVideoReceiveConfig(
|
||||
size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
return GetVideoReceiveConfig(events_[index]);
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetVideoReceiveConfig(
|
||||
const rtclog::Event& event) const {
|
||||
rtclog::StreamConfig config;
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT);
|
||||
RTC_CHECK(event.has_video_receiver_config());
|
||||
const rtclog::VideoReceiveConfig& receiver_config =
|
||||
event.video_receiver_config();
|
||||
// Get SSRCs.
|
||||
RTC_CHECK(receiver_config.has_remote_ssrc());
|
||||
config.remote_ssrc = receiver_config.remote_ssrc();
|
||||
RTC_CHECK(receiver_config.has_local_ssrc());
|
||||
config.local_ssrc = receiver_config.local_ssrc();
|
||||
config.rtx_ssrc = 0;
|
||||
// Get RTCP settings.
|
||||
RTC_CHECK(receiver_config.has_rtcp_mode());
|
||||
config.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode());
|
||||
RTC_CHECK(receiver_config.has_remb());
|
||||
config.remb = receiver_config.remb();
|
||||
|
||||
// Get RTX map.
|
||||
std::map<uint32_t, const rtclog::RtxConfig> rtx_map;
|
||||
for (int i = 0; i < receiver_config.rtx_map_size(); i++) {
|
||||
const rtclog::RtxMap& map = receiver_config.rtx_map(i);
|
||||
RTC_CHECK(map.has_payload_type());
|
||||
RTC_CHECK(map.has_config());
|
||||
RTC_CHECK(map.config().has_rtx_ssrc());
|
||||
RTC_CHECK(map.config().has_rtx_payload_type());
|
||||
rtx_map.insert(std::make_pair(map.payload_type(), map.config()));
|
||||
}
|
||||
|
||||
// Get header extensions.
|
||||
GetHeaderExtensions(&config.rtp_extensions,
|
||||
receiver_config.header_extensions());
|
||||
// Get decoders.
|
||||
config.codecs.clear();
|
||||
for (int i = 0; i < receiver_config.decoders_size(); i++) {
|
||||
RTC_CHECK(receiver_config.decoders(i).has_name());
|
||||
RTC_CHECK(receiver_config.decoders(i).has_payload_type());
|
||||
int rtx_payload_type = 0;
|
||||
auto rtx_it = rtx_map.find(receiver_config.decoders(i).payload_type());
|
||||
if (rtx_it != rtx_map.end()) {
|
||||
rtx_payload_type = rtx_it->second.rtx_payload_type();
|
||||
if (config.rtx_ssrc != 0 &&
|
||||
config.rtx_ssrc != rtx_it->second.rtx_ssrc()) {
|
||||
LOG(LS_WARNING)
|
||||
<< "RtcEventLog protobuf contained different SSRCs for "
|
||||
"different received RTX payload types. Will only use "
|
||||
"rtx_ssrc = "
|
||||
<< config.rtx_ssrc << ".";
|
||||
} else {
|
||||
config.rtx_ssrc = rtx_it->second.rtx_ssrc();
|
||||
}
|
||||
}
|
||||
config.codecs.emplace_back(receiver_config.decoders(i).name(),
|
||||
receiver_config.decoders(i).payload_type(),
|
||||
rtx_payload_type);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
std::vector<rtclog::StreamConfig> ParsedRtcEventLog::GetVideoSendConfig(
|
||||
size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
return GetVideoSendConfig(events_[index]);
|
||||
}
|
||||
|
||||
std::vector<rtclog::StreamConfig> ParsedRtcEventLog::GetVideoSendConfig(
|
||||
const rtclog::Event& event) const {
|
||||
std::vector<rtclog::StreamConfig> configs;
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_SENDER_CONFIG_EVENT);
|
||||
RTC_CHECK(event.has_video_sender_config());
|
||||
const rtclog::VideoSendConfig& sender_config = event.video_sender_config();
|
||||
if (sender_config.rtx_ssrcs_size() > 0 &&
|
||||
sender_config.ssrcs_size() != sender_config.rtx_ssrcs_size()) {
|
||||
LOG(WARNING) << "VideoSendConfig is configured for RTX but the number of "
|
||||
"SSRCs doesn't match the number of RTX SSRCs.";
|
||||
}
|
||||
configs.resize(sender_config.ssrcs_size());
|
||||
for (int i = 0; i < sender_config.ssrcs_size(); i++) {
|
||||
// Get SSRCs.
|
||||
configs[i].local_ssrc = sender_config.ssrcs(i);
|
||||
if (sender_config.rtx_ssrcs_size() > 0 &&
|
||||
i < sender_config.rtx_ssrcs_size()) {
|
||||
RTC_CHECK(sender_config.has_rtx_payload_type());
|
||||
configs[i].rtx_ssrc = sender_config.rtx_ssrcs(i);
|
||||
}
|
||||
// Get header extensions.
|
||||
GetHeaderExtensions(&configs[i].rtp_extensions,
|
||||
sender_config.header_extensions());
|
||||
|
||||
// Get the codec.
|
||||
RTC_CHECK(sender_config.has_encoder());
|
||||
RTC_CHECK(sender_config.encoder().has_name());
|
||||
RTC_CHECK(sender_config.encoder().has_payload_type());
|
||||
configs[i].codecs.emplace_back(
|
||||
sender_config.encoder().name(), sender_config.encoder().payload_type(),
|
||||
sender_config.has_rtx_payload_type() ? sender_config.rtx_payload_type()
|
||||
: 0);
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetAudioReceiveConfig(
|
||||
size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
return GetAudioReceiveConfig(events_[index]);
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetAudioReceiveConfig(
|
||||
const rtclog::Event& event) const {
|
||||
rtclog::StreamConfig config;
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT);
|
||||
RTC_CHECK(event.has_audio_receiver_config());
|
||||
const rtclog::AudioReceiveConfig& receiver_config =
|
||||
event.audio_receiver_config();
|
||||
// Get SSRCs.
|
||||
RTC_CHECK(receiver_config.has_remote_ssrc());
|
||||
config.remote_ssrc = receiver_config.remote_ssrc();
|
||||
RTC_CHECK(receiver_config.has_local_ssrc());
|
||||
config.local_ssrc = receiver_config.local_ssrc();
|
||||
// Get header extensions.
|
||||
GetHeaderExtensions(&config.rtp_extensions,
|
||||
receiver_config.header_extensions());
|
||||
return config;
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetAudioSendConfig(size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
return GetAudioSendConfig(events_[index]);
|
||||
}
|
||||
|
||||
rtclog::StreamConfig ParsedRtcEventLog::GetAudioSendConfig(
|
||||
const rtclog::Event& event) const {
|
||||
rtclog::StreamConfig config;
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_SENDER_CONFIG_EVENT);
|
||||
RTC_CHECK(event.has_audio_sender_config());
|
||||
const rtclog::AudioSendConfig& sender_config = event.audio_sender_config();
|
||||
// Get SSRCs.
|
||||
RTC_CHECK(sender_config.has_ssrc());
|
||||
config.local_ssrc = sender_config.ssrc();
|
||||
// Get header extensions.
|
||||
GetHeaderExtensions(&config.rtp_extensions,
|
||||
sender_config.header_extensions());
|
||||
return config;
|
||||
}
|
||||
|
||||
void ParsedRtcEventLog::GetAudioPlayout(size_t index, uint32_t* ssrc) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_PLAYOUT_EVENT);
|
||||
RTC_CHECK(event.has_audio_playout_event());
|
||||
const rtclog::AudioPlayoutEvent& loss_event = event.audio_playout_event();
|
||||
RTC_CHECK(loss_event.has_local_ssrc());
|
||||
if (ssrc != nullptr) {
|
||||
*ssrc = loss_event.local_ssrc();
|
||||
}
|
||||
}
|
||||
|
||||
void ParsedRtcEventLog::GetLossBasedBweUpdate(size_t index,
|
||||
int32_t* bitrate_bps,
|
||||
uint8_t* fraction_loss,
|
||||
int32_t* total_packets) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::LOSS_BASED_BWE_UPDATE);
|
||||
RTC_CHECK(event.has_loss_based_bwe_update());
|
||||
const rtclog::LossBasedBweUpdate& loss_event = event.loss_based_bwe_update();
|
||||
RTC_CHECK(loss_event.has_bitrate_bps());
|
||||
if (bitrate_bps != nullptr) {
|
||||
*bitrate_bps = loss_event.bitrate_bps();
|
||||
}
|
||||
RTC_CHECK(loss_event.has_fraction_loss());
|
||||
if (fraction_loss != nullptr) {
|
||||
*fraction_loss = loss_event.fraction_loss();
|
||||
}
|
||||
RTC_CHECK(loss_event.has_total_packets());
|
||||
if (total_packets != nullptr) {
|
||||
*total_packets = loss_event.total_packets();
|
||||
}
|
||||
}
|
||||
|
||||
ParsedRtcEventLog::BweDelayBasedUpdate
|
||||
ParsedRtcEventLog::GetDelayBasedBweUpdate(size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::DELAY_BASED_BWE_UPDATE);
|
||||
RTC_CHECK(event.has_delay_based_bwe_update());
|
||||
const rtclog::DelayBasedBweUpdate& delay_event =
|
||||
event.delay_based_bwe_update();
|
||||
|
||||
BweDelayBasedUpdate res;
|
||||
res.timestamp = GetTimestamp(index);
|
||||
RTC_CHECK(delay_event.has_bitrate_bps());
|
||||
res.bitrate_bps = delay_event.bitrate_bps();
|
||||
RTC_CHECK(delay_event.has_detector_state());
|
||||
res.detector_state = GetRuntimeDetectorState(delay_event.detector_state());
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParsedRtcEventLog::GetAudioNetworkAdaptation(
|
||||
size_t index,
|
||||
AudioEncoderRuntimeConfig* config) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT);
|
||||
RTC_CHECK(event.has_audio_network_adaptation());
|
||||
const rtclog::AudioNetworkAdaptation& ana_event =
|
||||
event.audio_network_adaptation();
|
||||
if (ana_event.has_bitrate_bps())
|
||||
config->bitrate_bps = rtc::Optional<int>(ana_event.bitrate_bps());
|
||||
if (ana_event.has_enable_fec())
|
||||
config->enable_fec = rtc::Optional<bool>(ana_event.enable_fec());
|
||||
if (ana_event.has_enable_dtx())
|
||||
config->enable_dtx = rtc::Optional<bool>(ana_event.enable_dtx());
|
||||
if (ana_event.has_frame_length_ms())
|
||||
config->frame_length_ms = rtc::Optional<int>(ana_event.frame_length_ms());
|
||||
if (ana_event.has_num_channels())
|
||||
config->num_channels = rtc::Optional<size_t>(ana_event.num_channels());
|
||||
if (ana_event.has_uplink_packet_loss_fraction())
|
||||
config->uplink_packet_loss_fraction =
|
||||
rtc::Optional<float>(ana_event.uplink_packet_loss_fraction());
|
||||
}
|
||||
|
||||
ParsedRtcEventLog::BweProbeClusterCreatedEvent
|
||||
ParsedRtcEventLog::GetBweProbeClusterCreated(size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT);
|
||||
RTC_CHECK(event.has_probe_cluster());
|
||||
const rtclog::BweProbeCluster& pcc_event = event.probe_cluster();
|
||||
BweProbeClusterCreatedEvent res;
|
||||
res.timestamp = GetTimestamp(index);
|
||||
RTC_CHECK(pcc_event.has_id());
|
||||
res.id = pcc_event.id();
|
||||
RTC_CHECK(pcc_event.has_bitrate_bps());
|
||||
res.bitrate_bps = pcc_event.bitrate_bps();
|
||||
RTC_CHECK(pcc_event.has_min_packets());
|
||||
res.min_packets = pcc_event.min_packets();
|
||||
RTC_CHECK(pcc_event.has_min_bytes());
|
||||
res.min_bytes = pcc_event.min_bytes();
|
||||
return res;
|
||||
}
|
||||
|
||||
ParsedRtcEventLog::BweProbeResultEvent ParsedRtcEventLog::GetBweProbeResult(
|
||||
size_t index) const {
|
||||
RTC_CHECK_LT(index, GetNumberOfEvents());
|
||||
const rtclog::Event& event = events_[index];
|
||||
RTC_CHECK(event.has_type());
|
||||
RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PROBE_RESULT_EVENT);
|
||||
RTC_CHECK(event.has_probe_result());
|
||||
const rtclog::BweProbeResult& pr_event = event.probe_result();
|
||||
BweProbeResultEvent res;
|
||||
res.timestamp = GetTimestamp(index);
|
||||
RTC_CHECK(pr_event.has_id());
|
||||
res.id = pr_event.id();
|
||||
|
||||
RTC_CHECK(pr_event.has_result());
|
||||
if (pr_event.result() == rtclog::BweProbeResult::SUCCESS) {
|
||||
RTC_CHECK(pr_event.has_bitrate_bps());
|
||||
res.bitrate_bps = rtc::Optional<uint64_t>(pr_event.bitrate_bps());
|
||||
} else if (pr_event.result() ==
|
||||
rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL) {
|
||||
res.failure_reason =
|
||||
rtc::Optional<ProbeFailureReason>(kInvalidSendReceiveInterval);
|
||||
} else if (pr_event.result() ==
|
||||
rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO) {
|
||||
res.failure_reason =
|
||||
rtc::Optional<ProbeFailureReason>(kInvalidSendReceiveRatio);
|
||||
} else if (pr_event.result() == rtclog::BweProbeResult::TIMEOUT) {
|
||||
res.failure_reason = rtc::Optional<ProbeFailureReason>(kTimeout);
|
||||
} else {
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Returns the MediaType for registered SSRCs. Search from the end to use last
|
||||
// registered types first.
|
||||
ParsedRtcEventLog::MediaType ParsedRtcEventLog::GetMediaType(
|
||||
uint32_t ssrc,
|
||||
PacketDirection direction) const {
|
||||
for (auto rit = streams_.rbegin(); rit != streams_.rend(); ++rit) {
|
||||
if (rit->ssrc == ssrc && rit->direction == direction)
|
||||
return rit->media_type;
|
||||
}
|
||||
return MediaType::ANY;
|
||||
}
|
||||
} // namespace webrtc
|
||||
212
logging/rtc_event_log/rtc_event_log_parser.h
Normal file
212
logging/rtc_event_log/rtc_event_log_parser.h
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility> // pair
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/call/video_receive_stream.h"
|
||||
#include "webrtc/call/video_send_stream.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/modules/rtp_rtcp/include/rtp_header_extension_map.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
||||
#include "webrtc/rtc_base/ignore_wundef.h"
|
||||
|
||||
// Files generated at build-time by the protobuf compiler.
|
||||
RTC_PUSH_IGNORING_WUNDEF()
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#else
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#endif
|
||||
RTC_POP_IGNORING_WUNDEF()
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
enum class MediaType;
|
||||
|
||||
class ParsedRtcEventLog {
|
||||
friend class RtcEventLogTestHelper;
|
||||
|
||||
public:
|
||||
struct BweProbeClusterCreatedEvent {
|
||||
uint64_t timestamp;
|
||||
uint32_t id;
|
||||
uint64_t bitrate_bps;
|
||||
uint32_t min_packets;
|
||||
uint32_t min_bytes;
|
||||
};
|
||||
|
||||
struct BweProbeResultEvent {
|
||||
uint64_t timestamp;
|
||||
uint32_t id;
|
||||
rtc::Optional<uint64_t> bitrate_bps;
|
||||
rtc::Optional<ProbeFailureReason> failure_reason;
|
||||
};
|
||||
|
||||
struct BweDelayBasedUpdate {
|
||||
uint64_t timestamp;
|
||||
int32_t bitrate_bps;
|
||||
BandwidthUsage detector_state;
|
||||
};
|
||||
|
||||
enum EventType {
|
||||
UNKNOWN_EVENT = 0,
|
||||
LOG_START = 1,
|
||||
LOG_END = 2,
|
||||
RTP_EVENT = 3,
|
||||
RTCP_EVENT = 4,
|
||||
AUDIO_PLAYOUT_EVENT = 5,
|
||||
LOSS_BASED_BWE_UPDATE = 6,
|
||||
DELAY_BASED_BWE_UPDATE = 7,
|
||||
VIDEO_RECEIVER_CONFIG_EVENT = 8,
|
||||
VIDEO_SENDER_CONFIG_EVENT = 9,
|
||||
AUDIO_RECEIVER_CONFIG_EVENT = 10,
|
||||
AUDIO_SENDER_CONFIG_EVENT = 11,
|
||||
AUDIO_NETWORK_ADAPTATION_EVENT = 16,
|
||||
BWE_PROBE_CLUSTER_CREATED_EVENT = 17,
|
||||
BWE_PROBE_RESULT_EVENT = 18
|
||||
};
|
||||
|
||||
enum class MediaType { ANY, AUDIO, VIDEO, DATA };
|
||||
|
||||
// Reads an RtcEventLog file and returns true if parsing was successful.
|
||||
bool ParseFile(const std::string& file_name);
|
||||
|
||||
// Reads an RtcEventLog from a string and returns true if successful.
|
||||
bool ParseString(const std::string& s);
|
||||
|
||||
// Reads an RtcEventLog from an istream and returns true if successful.
|
||||
bool ParseStream(std::istream& stream);
|
||||
|
||||
// Returns the number of events in an EventStream.
|
||||
size_t GetNumberOfEvents() const;
|
||||
|
||||
// Reads the arrival timestamp (in microseconds) from a rtclog::Event.
|
||||
int64_t GetTimestamp(size_t index) const;
|
||||
|
||||
// Reads the event type of the rtclog::Event at |index|.
|
||||
EventType GetEventType(size_t index) const;
|
||||
|
||||
// Reads the header, direction, header length and packet length from the RTP
|
||||
// event at |index|, and stores the values in the corresponding output
|
||||
// parameters. Each output parameter can be set to nullptr if that value
|
||||
// isn't needed.
|
||||
// NB: The header must have space for at least IP_PACKET_SIZE bytes.
|
||||
// Returns: a pointer to a header extensions map acquired from parsing
|
||||
// corresponding Audio/Video Sender/Receiver config events.
|
||||
// Warning: if the same SSRC is reused by both video and audio streams during
|
||||
// call, extensions maps may be incorrect (the last one would be returned).
|
||||
webrtc::RtpHeaderExtensionMap* GetRtpHeader(size_t index,
|
||||
PacketDirection* incoming,
|
||||
uint8_t* header,
|
||||
size_t* header_length,
|
||||
size_t* total_length) const;
|
||||
|
||||
// Reads packet, direction and packet length from the RTCP event at |index|,
|
||||
// and stores the values in the corresponding output parameters.
|
||||
// Each output parameter can be set to nullptr if that value isn't needed.
|
||||
// NB: The packet must have space for at least IP_PACKET_SIZE bytes.
|
||||
void GetRtcpPacket(size_t index,
|
||||
PacketDirection* incoming,
|
||||
uint8_t* packet,
|
||||
size_t* length) const;
|
||||
|
||||
// Reads a video receive config event to a StreamConfig struct.
|
||||
// Only the fields that are stored in the protobuf will be written.
|
||||
rtclog::StreamConfig GetVideoReceiveConfig(size_t index) const;
|
||||
|
||||
// Reads a video send config event to a StreamConfig struct. If the proto
|
||||
// contains multiple SSRCs and RTX SSRCs (this used to be the case for
|
||||
// simulcast streams) then we return one StreamConfig per SSRC,RTX_SSRC pair.
|
||||
// Only the fields that are stored in the protobuf will be written.
|
||||
std::vector<rtclog::StreamConfig> GetVideoSendConfig(size_t index) const;
|
||||
|
||||
// Reads a audio receive config event to a StreamConfig struct.
|
||||
// Only the fields that are stored in the protobuf will be written.
|
||||
rtclog::StreamConfig GetAudioReceiveConfig(size_t index) const;
|
||||
|
||||
// Reads a config event to a StreamConfig struct.
|
||||
// Only the fields that are stored in the protobuf will be written.
|
||||
rtclog::StreamConfig GetAudioSendConfig(size_t index) const;
|
||||
|
||||
// Reads the SSRC from the audio playout event at |index|. The SSRC is stored
|
||||
// in the output parameter ssrc. The output parameter can be set to nullptr
|
||||
// and in that case the function only asserts that the event is well formed.
|
||||
void GetAudioPlayout(size_t index, uint32_t* ssrc) const;
|
||||
|
||||
// Reads bitrate, fraction loss (as defined in RFC 1889) and total number of
|
||||
// expected packets from the loss based BWE event at |index| and stores the
|
||||
// values in
|
||||
// the corresponding output parameters. Each output parameter can be set to
|
||||
// nullptr if that
|
||||
// value isn't needed.
|
||||
void GetLossBasedBweUpdate(size_t index,
|
||||
int32_t* bitrate_bps,
|
||||
uint8_t* fraction_loss,
|
||||
int32_t* total_packets) const;
|
||||
|
||||
// Reads bitrate and detector_state from the delay based BWE event at |index|
|
||||
// and stores the values in the corresponding output parameters. Each output
|
||||
// parameter can be set to nullptr if that
|
||||
// value isn't needed.
|
||||
BweDelayBasedUpdate GetDelayBasedBweUpdate(size_t index) const;
|
||||
|
||||
// Reads a audio network adaptation event to a (non-NULL)
|
||||
// AudioEncoderRuntimeConfig struct. Only the fields that are
|
||||
// stored in the protobuf will be written.
|
||||
void GetAudioNetworkAdaptation(size_t index,
|
||||
AudioEncoderRuntimeConfig* config) const;
|
||||
|
||||
BweProbeClusterCreatedEvent GetBweProbeClusterCreated(size_t index) const;
|
||||
|
||||
BweProbeResultEvent GetBweProbeResult(size_t index) const;
|
||||
|
||||
MediaType GetMediaType(uint32_t ssrc, PacketDirection direction) const;
|
||||
|
||||
private:
|
||||
rtclog::StreamConfig GetVideoReceiveConfig(const rtclog::Event& event) const;
|
||||
std::vector<rtclog::StreamConfig> GetVideoSendConfig(
|
||||
const rtclog::Event& event) const;
|
||||
rtclog::StreamConfig GetAudioReceiveConfig(const rtclog::Event& event) const;
|
||||
rtclog::StreamConfig GetAudioSendConfig(const rtclog::Event& event) const;
|
||||
|
||||
std::vector<rtclog::Event> events_;
|
||||
|
||||
struct Stream {
|
||||
Stream(uint32_t ssrc,
|
||||
MediaType media_type,
|
||||
webrtc::PacketDirection direction,
|
||||
webrtc::RtpHeaderExtensionMap map)
|
||||
: ssrc(ssrc),
|
||||
media_type(media_type),
|
||||
direction(direction),
|
||||
rtp_extensions_map(map) {}
|
||||
uint32_t ssrc;
|
||||
MediaType media_type;
|
||||
webrtc::PacketDirection direction;
|
||||
webrtc::RtpHeaderExtensionMap rtp_extensions_map;
|
||||
};
|
||||
|
||||
// All configured streams found in the event log.
|
||||
std::vector<Stream> streams_;
|
||||
|
||||
// To find configured extensions map for given stream, what are needed to
|
||||
// parse a header.
|
||||
typedef std::pair<uint32_t, webrtc::PacketDirection> StreamId;
|
||||
std::map<StreamId, webrtc::RtpHeaderExtensionMap*> rtp_extensions_maps_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_
|
||||
876
logging/rtc_event_log/rtc_event_log_unittest.cc
Normal file
876
logging/rtc_event_log/rtc_event_log_unittest.cc
Normal file
@ -0,0 +1,876 @@
|
||||
/*
|
||||
* 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 <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/call/call.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_parser.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h"
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/include/rtp_header_extension_map.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "webrtc/rtc_base/buffer.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/fakeclock.h"
|
||||
#include "webrtc/rtc_base/random.h"
|
||||
#include "webrtc/test/gtest.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
|
||||
// Files generated at build-time by the protobuf compiler.
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#else
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
const RTPExtensionType kExtensionTypes[] = {
|
||||
RTPExtensionType::kRtpExtensionTransmissionTimeOffset,
|
||||
RTPExtensionType::kRtpExtensionAudioLevel,
|
||||
RTPExtensionType::kRtpExtensionAbsoluteSendTime,
|
||||
RTPExtensionType::kRtpExtensionVideoRotation,
|
||||
RTPExtensionType::kRtpExtensionTransportSequenceNumber};
|
||||
const char* kExtensionNames[] = {
|
||||
RtpExtension::kTimestampOffsetUri, RtpExtension::kAudioLevelUri,
|
||||
RtpExtension::kAbsSendTimeUri, RtpExtension::kVideoRotationUri,
|
||||
RtpExtension::kTransportSequenceNumberUri};
|
||||
const size_t kNumExtensions = 5;
|
||||
|
||||
void PrintActualEvents(const ParsedRtcEventLog& parsed_log) {
|
||||
std::map<int, size_t> actual_event_counts;
|
||||
for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
|
||||
actual_event_counts[parsed_log.GetEventType(i)]++;
|
||||
}
|
||||
printf("Actual events: ");
|
||||
for (auto kv : actual_event_counts) {
|
||||
printf("%d_count = %zu, ", kv.first, kv.second);
|
||||
}
|
||||
printf("\n");
|
||||
for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
|
||||
printf("%4d ", parsed_log.GetEventType(i));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void PrintExpectedEvents(size_t rtp_count,
|
||||
size_t rtcp_count,
|
||||
size_t playout_count,
|
||||
size_t bwe_loss_count) {
|
||||
printf(
|
||||
"Expected events: rtp_count = %zu, rtcp_count = %zu,"
|
||||
"playout_count = %zu, bwe_loss_count = %zu\n",
|
||||
rtp_count, rtcp_count, playout_count, bwe_loss_count);
|
||||
size_t rtcp_index = 1, playout_index = 1, bwe_loss_index = 1;
|
||||
printf("strt cfg cfg ");
|
||||
for (size_t i = 1; i <= rtp_count; i++) {
|
||||
printf(" rtp ");
|
||||
if (i * rtcp_count >= rtcp_index * rtp_count) {
|
||||
printf("rtcp ");
|
||||
rtcp_index++;
|
||||
}
|
||||
if (i * playout_count >= playout_index * rtp_count) {
|
||||
printf("play ");
|
||||
playout_index++;
|
||||
}
|
||||
if (i * bwe_loss_count >= bwe_loss_index * rtp_count) {
|
||||
printf("loss ");
|
||||
bwe_loss_index++;
|
||||
}
|
||||
}
|
||||
printf("end \n");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
* Bit number i of extension_bitvector is set to indicate the
|
||||
* presence of extension number i from kExtensionTypes / kExtensionNames.
|
||||
* The least significant bit extension_bitvector has number 0.
|
||||
*/
|
||||
RtpPacketToSend GenerateRtpPacket(const RtpHeaderExtensionMap* extensions,
|
||||
uint32_t csrcs_count,
|
||||
size_t packet_size,
|
||||
Random* prng) {
|
||||
RTC_CHECK_GE(packet_size, 16 + 4 * csrcs_count + 4 * kNumExtensions);
|
||||
|
||||
std::vector<uint32_t> csrcs;
|
||||
for (unsigned i = 0; i < csrcs_count; i++) {
|
||||
csrcs.push_back(prng->Rand<uint32_t>());
|
||||
}
|
||||
|
||||
RtpPacketToSend rtp_packet(extensions, packet_size);
|
||||
rtp_packet.SetPayloadType(prng->Rand(127));
|
||||
rtp_packet.SetMarker(prng->Rand<bool>());
|
||||
rtp_packet.SetSequenceNumber(prng->Rand<uint16_t>());
|
||||
rtp_packet.SetSsrc(prng->Rand<uint32_t>());
|
||||
rtp_packet.SetTimestamp(prng->Rand<uint32_t>());
|
||||
rtp_packet.SetCsrcs(csrcs);
|
||||
|
||||
rtp_packet.SetExtension<TransmissionOffset>(prng->Rand(0x00ffffff));
|
||||
rtp_packet.SetExtension<AudioLevel>(prng->Rand<bool>(), prng->Rand(127));
|
||||
rtp_packet.SetExtension<AbsoluteSendTime>(prng->Rand(0x00ffffff));
|
||||
rtp_packet.SetExtension<VideoOrientation>(prng->Rand(2));
|
||||
rtp_packet.SetExtension<TransportSequenceNumber>(prng->Rand<uint16_t>());
|
||||
|
||||
size_t payload_size = packet_size - rtp_packet.headers_size();
|
||||
uint8_t* payload = rtp_packet.AllocatePayload(payload_size);
|
||||
for (size_t i = 0; i < payload_size; i++) {
|
||||
payload[i] = prng->Rand<uint8_t>();
|
||||
}
|
||||
return rtp_packet;
|
||||
}
|
||||
|
||||
rtc::Buffer GenerateRtcpPacket(Random* prng) {
|
||||
rtcp::ReportBlock report_block;
|
||||
report_block.SetMediaSsrc(prng->Rand<uint32_t>()); // Remote SSRC.
|
||||
report_block.SetFractionLost(prng->Rand(50));
|
||||
|
||||
rtcp::SenderReport sender_report;
|
||||
sender_report.SetSenderSsrc(prng->Rand<uint32_t>());
|
||||
sender_report.SetNtp(NtpTime(prng->Rand<uint32_t>(), prng->Rand<uint32_t>()));
|
||||
sender_report.SetPacketCount(prng->Rand<uint32_t>());
|
||||
sender_report.AddReportBlock(report_block);
|
||||
|
||||
return sender_report.Build();
|
||||
}
|
||||
|
||||
void GenerateVideoReceiveConfig(uint32_t extensions_bitvector,
|
||||
rtclog::StreamConfig* config,
|
||||
Random* prng) {
|
||||
// Add SSRCs for the stream.
|
||||
config->remote_ssrc = prng->Rand<uint32_t>();
|
||||
config->local_ssrc = prng->Rand<uint32_t>();
|
||||
// Add extensions and settings for RTCP.
|
||||
config->rtcp_mode =
|
||||
prng->Rand<bool>() ? RtcpMode::kCompound : RtcpMode::kReducedSize;
|
||||
config->remb = prng->Rand<bool>();
|
||||
config->rtx_ssrc = prng->Rand<uint32_t>();
|
||||
config->codecs.emplace_back(prng->Rand<bool>() ? "VP8" : "H264",
|
||||
prng->Rand(1, 127), prng->Rand(1, 127));
|
||||
// Add header extensions.
|
||||
for (unsigned i = 0; i < kNumExtensions; i++) {
|
||||
if (extensions_bitvector & (1u << i)) {
|
||||
config->rtp_extensions.emplace_back(kExtensionNames[i],
|
||||
prng->Rand<int>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateVideoSendConfig(uint32_t extensions_bitvector,
|
||||
rtclog::StreamConfig* config,
|
||||
Random* prng) {
|
||||
config->codecs.emplace_back(prng->Rand<bool>() ? "VP8" : "H264",
|
||||
prng->Rand(1, 127), prng->Rand(1, 127));
|
||||
config->local_ssrc = prng->Rand<uint32_t>();
|
||||
config->rtx_ssrc = prng->Rand<uint32_t>();
|
||||
// Add header extensions.
|
||||
for (unsigned i = 0; i < kNumExtensions; i++) {
|
||||
if (extensions_bitvector & (1u << i)) {
|
||||
config->rtp_extensions.push_back(
|
||||
RtpExtension(kExtensionNames[i], prng->Rand<int>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateAudioReceiveConfig(uint32_t extensions_bitvector,
|
||||
rtclog::StreamConfig* config,
|
||||
Random* prng) {
|
||||
// Add SSRCs for the stream.
|
||||
config->remote_ssrc = prng->Rand<uint32_t>();
|
||||
config->local_ssrc = prng->Rand<uint32_t>();
|
||||
// Add header extensions.
|
||||
for (unsigned i = 0; i < kNumExtensions; i++) {
|
||||
if (extensions_bitvector & (1u << i)) {
|
||||
config->rtp_extensions.push_back(
|
||||
RtpExtension(kExtensionNames[i], prng->Rand<int>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateAudioSendConfig(uint32_t extensions_bitvector,
|
||||
rtclog::StreamConfig* config,
|
||||
Random* prng) {
|
||||
// Add SSRC to the stream.
|
||||
config->local_ssrc = prng->Rand<uint32_t>();
|
||||
// Add header extensions.
|
||||
for (unsigned i = 0; i < kNumExtensions; i++) {
|
||||
if (extensions_bitvector & (1u << i)) {
|
||||
config->rtp_extensions.push_back(
|
||||
RtpExtension(kExtensionNames[i], prng->Rand<int>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateAudioNetworkAdaptation(uint32_t extensions_bitvector,
|
||||
AudioEncoderRuntimeConfig* config,
|
||||
Random* prng) {
|
||||
config->bitrate_bps = rtc::Optional<int>(prng->Rand(0, 3000000));
|
||||
config->enable_fec = rtc::Optional<bool>(prng->Rand<bool>());
|
||||
config->enable_dtx = rtc::Optional<bool>(prng->Rand<bool>());
|
||||
config->frame_length_ms = rtc::Optional<int>(prng->Rand(10, 120));
|
||||
config->num_channels = rtc::Optional<size_t>(prng->Rand(1, 2));
|
||||
config->uplink_packet_loss_fraction =
|
||||
rtc::Optional<float>(prng->Rand<float>());
|
||||
}
|
||||
|
||||
// Test for the RtcEventLog class. Dumps some RTP packets and other events
|
||||
// to disk, then reads them back to see if they match.
|
||||
void LogSessionAndReadBack(size_t rtp_count,
|
||||
size_t rtcp_count,
|
||||
size_t playout_count,
|
||||
size_t bwe_loss_count,
|
||||
uint32_t extensions_bitvector,
|
||||
uint32_t csrcs_count,
|
||||
unsigned int random_seed) {
|
||||
ASSERT_LE(rtcp_count, rtp_count);
|
||||
ASSERT_LE(playout_count, rtp_count);
|
||||
ASSERT_LE(bwe_loss_count, rtp_count);
|
||||
std::vector<RtpPacketToSend> rtp_packets;
|
||||
std::vector<rtc::Buffer> rtcp_packets;
|
||||
std::vector<uint32_t> playout_ssrcs;
|
||||
std::vector<std::pair<int32_t, uint8_t> > bwe_loss_updates;
|
||||
|
||||
rtclog::StreamConfig receiver_config;
|
||||
rtclog::StreamConfig sender_config;
|
||||
|
||||
Random prng(random_seed);
|
||||
|
||||
// Initialize rtp header extensions to be used in generated rtp packets.
|
||||
RtpHeaderExtensionMap extensions;
|
||||
for (unsigned i = 0; i < kNumExtensions; i++) {
|
||||
if (extensions_bitvector & (1u << i)) {
|
||||
extensions.Register(kExtensionTypes[i], i + 1);
|
||||
}
|
||||
}
|
||||
// Create rtp_count RTP packets containing random data.
|
||||
for (size_t i = 0; i < rtp_count; i++) {
|
||||
size_t packet_size = prng.Rand(1000, 1100);
|
||||
rtp_packets.push_back(
|
||||
GenerateRtpPacket(&extensions, csrcs_count, packet_size, &prng));
|
||||
}
|
||||
// Create rtcp_count RTCP packets containing random data.
|
||||
for (size_t i = 0; i < rtcp_count; i++) {
|
||||
rtcp_packets.push_back(GenerateRtcpPacket(&prng));
|
||||
}
|
||||
// Create playout_count random SSRCs to use when logging AudioPlayout events.
|
||||
for (size_t i = 0; i < playout_count; i++) {
|
||||
playout_ssrcs.push_back(prng.Rand<uint32_t>());
|
||||
}
|
||||
// Create bwe_loss_count random bitrate updates for LossBasedBwe.
|
||||
for (size_t i = 0; i < bwe_loss_count; i++) {
|
||||
bwe_loss_updates.push_back(
|
||||
std::make_pair(prng.Rand<int32_t>(), prng.Rand<uint8_t>()));
|
||||
}
|
||||
// Create configurations for the video streams.
|
||||
GenerateVideoReceiveConfig(extensions_bitvector, &receiver_config, &prng);
|
||||
GenerateVideoSendConfig(extensions_bitvector, &sender_config, &prng);
|
||||
const int config_count = 2;
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
// When log_dumper goes out of scope, it causes the log file to be flushed
|
||||
// to disk.
|
||||
{
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
log_dumper->LogVideoReceiveStreamConfig(receiver_config);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogVideoSendStreamConfig(sender_config);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
size_t rtcp_index = 1;
|
||||
size_t playout_index = 1;
|
||||
size_t bwe_loss_index = 1;
|
||||
for (size_t i = 1; i <= rtp_count; i++) {
|
||||
log_dumper->LogRtpHeader(
|
||||
(i % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
|
||||
rtp_packets[i - 1].data(), rtp_packets[i - 1].size());
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
if (i * rtcp_count >= rtcp_index * rtp_count) {
|
||||
log_dumper->LogRtcpPacket(
|
||||
(rtcp_index % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
|
||||
rtcp_packets[rtcp_index - 1].data(),
|
||||
rtcp_packets[rtcp_index - 1].size());
|
||||
rtcp_index++;
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
}
|
||||
if (i * playout_count >= playout_index * rtp_count) {
|
||||
log_dumper->LogAudioPlayout(playout_ssrcs[playout_index - 1]);
|
||||
playout_index++;
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
}
|
||||
if (i * bwe_loss_count >= bwe_loss_index * rtp_count) {
|
||||
log_dumper->LogLossBasedBweUpdate(
|
||||
bwe_loss_updates[bwe_loss_index - 1].first,
|
||||
bwe_loss_updates[bwe_loss_index - 1].second, i);
|
||||
bwe_loss_index++;
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
}
|
||||
if (i == rtp_count / 2) {
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
}
|
||||
}
|
||||
log_dumper->StopLogging();
|
||||
}
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down. For RTCP we log the full packets, but for
|
||||
// RTP we should only log the header.
|
||||
const size_t event_count = config_count + playout_count + bwe_loss_count +
|
||||
rtcp_count + rtp_count + 2;
|
||||
EXPECT_GE(1000u, event_count); // The events must fit in the message queue.
|
||||
EXPECT_EQ(event_count, parsed_log.GetNumberOfEvents());
|
||||
if (event_count != parsed_log.GetNumberOfEvents()) {
|
||||
// Print the expected and actual event types for easier debugging.
|
||||
PrintActualEvents(parsed_log);
|
||||
PrintExpectedEvents(rtp_count, rtcp_count, playout_count, bwe_loss_count);
|
||||
}
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyVideoReceiveStreamConfig(parsed_log, 1,
|
||||
receiver_config);
|
||||
RtcEventLogTestHelper::VerifyVideoSendStreamConfig(parsed_log, 2,
|
||||
sender_config);
|
||||
size_t event_index = config_count + 1;
|
||||
size_t rtcp_index = 1;
|
||||
size_t playout_index = 1;
|
||||
size_t bwe_loss_index = 1;
|
||||
for (size_t i = 1; i <= rtp_count; i++) {
|
||||
RtcEventLogTestHelper::VerifyRtpEvent(
|
||||
parsed_log, event_index,
|
||||
(i % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
|
||||
rtp_packets[i - 1].data(), rtp_packets[i - 1].headers_size(),
|
||||
rtp_packets[i - 1].size());
|
||||
event_index++;
|
||||
if (i * rtcp_count >= rtcp_index * rtp_count) {
|
||||
RtcEventLogTestHelper::VerifyRtcpEvent(
|
||||
parsed_log, event_index,
|
||||
rtcp_index % 2 == 0 ? kIncomingPacket : kOutgoingPacket,
|
||||
rtcp_packets[rtcp_index - 1].data(),
|
||||
rtcp_packets[rtcp_index - 1].size());
|
||||
event_index++;
|
||||
rtcp_index++;
|
||||
}
|
||||
if (i * playout_count >= playout_index * rtp_count) {
|
||||
RtcEventLogTestHelper::VerifyPlayoutEvent(
|
||||
parsed_log, event_index, playout_ssrcs[playout_index - 1]);
|
||||
event_index++;
|
||||
playout_index++;
|
||||
}
|
||||
if (i * bwe_loss_count >= bwe_loss_index * rtp_count) {
|
||||
RtcEventLogTestHelper::VerifyBweLossEvent(
|
||||
parsed_log, event_index, bwe_loss_updates[bwe_loss_index - 1].first,
|
||||
bwe_loss_updates[bwe_loss_index - 1].second, i);
|
||||
event_index++;
|
||||
bwe_loss_index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogSessionAndReadBack) {
|
||||
// Log 5 RTP, 2 RTCP, 0 playout events and 0 BWE events
|
||||
// with no header extensions or CSRCS.
|
||||
LogSessionAndReadBack(5, 2, 0, 0, 0, 0, 321);
|
||||
|
||||
// Enable AbsSendTime and TransportSequenceNumbers.
|
||||
uint32_t extensions = 0;
|
||||
for (uint32_t i = 0; i < kNumExtensions; i++) {
|
||||
if (kExtensionTypes[i] == RTPExtensionType::kRtpExtensionAbsoluteSendTime ||
|
||||
kExtensionTypes[i] ==
|
||||
RTPExtensionType::kRtpExtensionTransportSequenceNumber) {
|
||||
extensions |= 1u << i;
|
||||
}
|
||||
}
|
||||
LogSessionAndReadBack(8, 2, 0, 0, extensions, 0, 3141592653u);
|
||||
|
||||
extensions = (1u << kNumExtensions) - 1; // Enable all header extensions.
|
||||
LogSessionAndReadBack(9, 2, 3, 2, extensions, 2, 2718281828u);
|
||||
|
||||
// Try all combinations of header extensions and up to 2 CSRCS.
|
||||
for (extensions = 0; extensions < (1u << kNumExtensions); extensions++) {
|
||||
for (uint32_t csrcs_count = 0; csrcs_count < 3; csrcs_count++) {
|
||||
LogSessionAndReadBack(5 + extensions, // Number of RTP packets.
|
||||
2 + csrcs_count, // Number of RTCP packets.
|
||||
3 + csrcs_count, // Number of playout events.
|
||||
1 + csrcs_count, // Number of BWE loss events.
|
||||
extensions, // Bit vector choosing extensions.
|
||||
csrcs_count, // Number of contributing sources.
|
||||
extensions * 3 + csrcs_count + 1); // Random seed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogEventAndReadBack) {
|
||||
Random prng(987654321);
|
||||
|
||||
// Create one RTP and one RTCP packet containing random data.
|
||||
size_t packet_size = prng.Rand(1000, 1100);
|
||||
RtpPacketToSend rtp_packet =
|
||||
GenerateRtpPacket(nullptr, 0, packet_size, &prng);
|
||||
rtc::Buffer rtcp_packet = GenerateRtcpPacket(&prng);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
// Add RTP, start logging, add RTCP and then stop logging
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
|
||||
log_dumper->LogRtpHeader(kIncomingPacket, rtp_packet.data(),
|
||||
rtp_packet.size());
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
|
||||
log_dumper->LogRtcpPacket(kOutgoingPacket, rtcp_packet.data(),
|
||||
rtcp_packet.size());
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(4u, parsed_log.GetNumberOfEvents());
|
||||
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
|
||||
RtcEventLogTestHelper::VerifyRtpEvent(
|
||||
parsed_log, 1, kIncomingPacket, rtp_packet.data(),
|
||||
rtp_packet.headers_size(), rtp_packet.size());
|
||||
|
||||
RtcEventLogTestHelper::VerifyRtcpEvent(
|
||||
parsed_log, 2, kOutgoingPacket, rtcp_packet.data(), rtcp_packet.size());
|
||||
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 3);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogLossBasedBweUpdateAndReadBack) {
|
||||
Random prng(1234);
|
||||
|
||||
// Generate a random packet loss event.
|
||||
int32_t bitrate = prng.Rand(0, 10000000);
|
||||
uint8_t fraction_lost = prng.Rand<uint8_t>();
|
||||
int32_t total_packets = prng.Rand(1, 1000);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
// Start logging, add the packet loss event and then stop logging.
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogLossBasedBweUpdate(bitrate, fraction_lost, total_packets);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(3u, parsed_log.GetNumberOfEvents());
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyBweLossEvent(parsed_log, 1, bitrate,
|
||||
fraction_lost, total_packets);
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 2);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogDelayBasedBweUpdateAndReadBack) {
|
||||
Random prng(1234);
|
||||
|
||||
// Generate 3 random packet delay event.
|
||||
int32_t bitrate1 = prng.Rand(0, 10000000);
|
||||
int32_t bitrate2 = prng.Rand(0, 10000000);
|
||||
int32_t bitrate3 = prng.Rand(0, 10000000);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
// Start logging, add the packet delay events and then stop logging.
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogDelayBasedBweUpdate(bitrate1, BandwidthUsage::kBwNormal);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogDelayBasedBweUpdate(bitrate2, BandwidthUsage::kBwOverusing);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogDelayBasedBweUpdate(bitrate3, BandwidthUsage::kBwUnderusing);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(5u, parsed_log.GetNumberOfEvents());
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyBweDelayEvent(parsed_log, 1, bitrate1,
|
||||
BandwidthUsage::kBwNormal);
|
||||
RtcEventLogTestHelper::VerifyBweDelayEvent(parsed_log, 2, bitrate2,
|
||||
BandwidthUsage::kBwOverusing);
|
||||
RtcEventLogTestHelper::VerifyBweDelayEvent(parsed_log, 3, bitrate3,
|
||||
BandwidthUsage::kBwUnderusing);
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 4);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogProbeClusterCreatedAndReadBack) {
|
||||
Random prng(794613);
|
||||
|
||||
int bitrate_bps0 = prng.Rand(0, 10000000);
|
||||
int bitrate_bps1 = prng.Rand(0, 10000000);
|
||||
int bitrate_bps2 = prng.Rand(0, 10000000);
|
||||
int min_probes0 = prng.Rand(0, 100);
|
||||
int min_probes1 = prng.Rand(0, 100);
|
||||
int min_probes2 = prng.Rand(0, 100);
|
||||
int min_bytes0 = prng.Rand(0, 10000);
|
||||
int min_bytes1 = prng.Rand(0, 10000);
|
||||
int min_bytes2 = prng.Rand(0, 10000);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
log_dumper->LogProbeClusterCreated(0, bitrate_bps0, min_probes0, min_bytes0);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeClusterCreated(1, bitrate_bps1, min_probes1, min_bytes1);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeClusterCreated(2, bitrate_bps2, min_probes2, min_bytes2);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(5u, parsed_log.GetNumberOfEvents());
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyBweProbeCluster(parsed_log, 1, 0, bitrate_bps0,
|
||||
min_probes0, min_bytes0);
|
||||
RtcEventLogTestHelper::VerifyBweProbeCluster(parsed_log, 2, 1, bitrate_bps1,
|
||||
min_probes1, min_bytes1);
|
||||
RtcEventLogTestHelper::VerifyBweProbeCluster(parsed_log, 3, 2, bitrate_bps2,
|
||||
min_probes2, min_bytes2);
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 4);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogProbeResultSuccessAndReadBack) {
|
||||
Random prng(192837);
|
||||
|
||||
int bitrate_bps0 = prng.Rand(0, 10000000);
|
||||
int bitrate_bps1 = prng.Rand(0, 10000000);
|
||||
int bitrate_bps2 = prng.Rand(0, 10000000);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
log_dumper->LogProbeResultSuccess(0, bitrate_bps0);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeResultSuccess(1, bitrate_bps1);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeResultSuccess(2, bitrate_bps2);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(5u, parsed_log.GetNumberOfEvents());
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyProbeResultSuccess(parsed_log, 1, 0,
|
||||
bitrate_bps0);
|
||||
RtcEventLogTestHelper::VerifyProbeResultSuccess(parsed_log, 2, 1,
|
||||
bitrate_bps1);
|
||||
RtcEventLogTestHelper::VerifyProbeResultSuccess(parsed_log, 3, 2,
|
||||
bitrate_bps2);
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 4);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogProbeResultFailureAndReadBack) {
|
||||
Random prng(192837);
|
||||
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
log_dumper->LogProbeResultFailure(
|
||||
0, ProbeFailureReason::kInvalidSendReceiveInterval);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeResultFailure(
|
||||
1, ProbeFailureReason::kInvalidSendReceiveRatio);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->LogProbeResultFailure(2, ProbeFailureReason::kTimeout);
|
||||
fake_clock.AdvanceTimeMicros(prng.Rand(1, 1000));
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Verify that what we read back from the event log is the same as
|
||||
// what we wrote down.
|
||||
EXPECT_EQ(5u, parsed_log.GetNumberOfEvents());
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
RtcEventLogTestHelper::VerifyProbeResultFailure(
|
||||
parsed_log, 1, 0, ProbeFailureReason::kInvalidSendReceiveInterval);
|
||||
RtcEventLogTestHelper::VerifyProbeResultFailure(
|
||||
parsed_log, 2, 1, ProbeFailureReason::kInvalidSendReceiveRatio);
|
||||
RtcEventLogTestHelper::VerifyProbeResultFailure(parsed_log, 3, 2,
|
||||
ProbeFailureReason::kTimeout);
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 4);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
|
||||
class ConfigReadWriteTest {
|
||||
public:
|
||||
ConfigReadWriteTest() : prng(987654321) {}
|
||||
virtual ~ConfigReadWriteTest() {}
|
||||
virtual void GenerateConfig(uint32_t extensions_bitvector) = 0;
|
||||
virtual void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) = 0;
|
||||
virtual void LogConfig(RtcEventLog* event_log) = 0;
|
||||
|
||||
void DoTest() {
|
||||
// Find the name of the current test, in order to use it as a temporary
|
||||
// filename.
|
||||
auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
const std::string temp_filename =
|
||||
test::OutputPath() + test_info->test_case_name() + test_info->name();
|
||||
|
||||
// Use all extensions.
|
||||
uint32_t extensions_bitvector = (1u << kNumExtensions) - 1;
|
||||
GenerateConfig(extensions_bitvector);
|
||||
|
||||
// Log a single config event and stop logging.
|
||||
rtc::ScopedFakeClock fake_clock;
|
||||
fake_clock.SetTimeMicros(prng.Rand<uint32_t>());
|
||||
std::unique_ptr<RtcEventLog> log_dumper(RtcEventLog::Create());
|
||||
|
||||
log_dumper->StartLogging(temp_filename, 10000000);
|
||||
LogConfig(log_dumper.get());
|
||||
log_dumper->StopLogging();
|
||||
|
||||
// Read the generated file from disk.
|
||||
ParsedRtcEventLog parsed_log;
|
||||
ASSERT_TRUE(parsed_log.ParseFile(temp_filename));
|
||||
|
||||
// Check the generated number of events.
|
||||
EXPECT_EQ(3u, parsed_log.GetNumberOfEvents());
|
||||
|
||||
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
|
||||
|
||||
// Verify that the parsed config struct matches the one that was logged.
|
||||
VerifyConfig(parsed_log, 1);
|
||||
|
||||
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 2);
|
||||
|
||||
// Clean up temporary file - can be pretty slow.
|
||||
remove(temp_filename.c_str());
|
||||
}
|
||||
Random prng;
|
||||
};
|
||||
|
||||
class AudioReceiveConfigReadWriteTest : public ConfigReadWriteTest {
|
||||
public:
|
||||
void GenerateConfig(uint32_t extensions_bitvector) override {
|
||||
GenerateAudioReceiveConfig(extensions_bitvector, &config, &prng);
|
||||
}
|
||||
void LogConfig(RtcEventLog* event_log) override {
|
||||
event_log->LogAudioReceiveStreamConfig(config);
|
||||
}
|
||||
void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) override {
|
||||
RtcEventLogTestHelper::VerifyAudioReceiveStreamConfig(parsed_log, index,
|
||||
config);
|
||||
}
|
||||
rtclog::StreamConfig config;
|
||||
};
|
||||
|
||||
class AudioSendConfigReadWriteTest : public ConfigReadWriteTest {
|
||||
public:
|
||||
AudioSendConfigReadWriteTest() {}
|
||||
void GenerateConfig(uint32_t extensions_bitvector) override {
|
||||
GenerateAudioSendConfig(extensions_bitvector, &config, &prng);
|
||||
}
|
||||
void LogConfig(RtcEventLog* event_log) override {
|
||||
event_log->LogAudioSendStreamConfig(config);
|
||||
}
|
||||
void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) override {
|
||||
RtcEventLogTestHelper::VerifyAudioSendStreamConfig(parsed_log, index,
|
||||
config);
|
||||
}
|
||||
rtclog::StreamConfig config;
|
||||
};
|
||||
|
||||
class VideoReceiveConfigReadWriteTest : public ConfigReadWriteTest {
|
||||
public:
|
||||
VideoReceiveConfigReadWriteTest() {}
|
||||
void GenerateConfig(uint32_t extensions_bitvector) override {
|
||||
GenerateVideoReceiveConfig(extensions_bitvector, &config, &prng);
|
||||
}
|
||||
void LogConfig(RtcEventLog* event_log) override {
|
||||
event_log->LogVideoReceiveStreamConfig(config);
|
||||
}
|
||||
void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) override {
|
||||
RtcEventLogTestHelper::VerifyVideoReceiveStreamConfig(parsed_log, index,
|
||||
config);
|
||||
}
|
||||
rtclog::StreamConfig config;
|
||||
};
|
||||
|
||||
class VideoSendConfigReadWriteTest : public ConfigReadWriteTest {
|
||||
public:
|
||||
VideoSendConfigReadWriteTest() {}
|
||||
void GenerateConfig(uint32_t extensions_bitvector) override {
|
||||
GenerateVideoSendConfig(extensions_bitvector, &config, &prng);
|
||||
}
|
||||
void LogConfig(RtcEventLog* event_log) override {
|
||||
event_log->LogVideoSendStreamConfig(config);
|
||||
}
|
||||
void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) override {
|
||||
RtcEventLogTestHelper::VerifyVideoSendStreamConfig(parsed_log, index,
|
||||
config);
|
||||
}
|
||||
rtclog::StreamConfig config;
|
||||
};
|
||||
|
||||
class AudioNetworkAdaptationReadWriteTest : public ConfigReadWriteTest {
|
||||
public:
|
||||
void GenerateConfig(uint32_t extensions_bitvector) override {
|
||||
GenerateAudioNetworkAdaptation(extensions_bitvector, &config, &prng);
|
||||
}
|
||||
void LogConfig(RtcEventLog* event_log) override {
|
||||
event_log->LogAudioNetworkAdaptation(config);
|
||||
}
|
||||
void VerifyConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) override {
|
||||
RtcEventLogTestHelper::VerifyAudioNetworkAdaptation(parsed_log, index,
|
||||
config);
|
||||
}
|
||||
AudioEncoderRuntimeConfig config;
|
||||
};
|
||||
|
||||
TEST(RtcEventLogTest, LogAudioReceiveConfig) {
|
||||
AudioReceiveConfigReadWriteTest test;
|
||||
test.DoTest();
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogAudioSendConfig) {
|
||||
AudioSendConfigReadWriteTest test;
|
||||
test.DoTest();
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogVideoReceiveConfig) {
|
||||
VideoReceiveConfigReadWriteTest test;
|
||||
test.DoTest();
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogVideoSendConfig) {
|
||||
VideoSendConfigReadWriteTest test;
|
||||
test.DoTest();
|
||||
}
|
||||
|
||||
TEST(RtcEventLogTest, LogAudioNetworkAdaptation) {
|
||||
AudioNetworkAdaptationReadWriteTest test;
|
||||
test.DoTest();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
573
logging/rtc_event_log/rtc_event_log_unittest_helper.cc
Normal file
573
logging/rtc_event_log/rtc_event_log_unittest_helper.cc
Normal file
@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 "webrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/test/gtest.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
|
||||
// Files generated at build-time by the protobuf compiler.
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#else
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log.pb.h"
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
BandwidthUsage GetRuntimeDetectorState(
|
||||
rtclog::DelayBasedBweUpdate::DetectorState detector_state) {
|
||||
switch (detector_state) {
|
||||
case rtclog::DelayBasedBweUpdate::BWE_NORMAL:
|
||||
return BandwidthUsage::kBwNormal;
|
||||
case rtclog::DelayBasedBweUpdate::BWE_UNDERUSING:
|
||||
return BandwidthUsage::kBwUnderusing;
|
||||
case rtclog::DelayBasedBweUpdate::BWE_OVERUSING:
|
||||
return BandwidthUsage::kBwOverusing;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return BandwidthUsage::kBwNormal;
|
||||
}
|
||||
|
||||
rtclog::BweProbeResult::ResultType GetProbeResultType(
|
||||
ProbeFailureReason failure_reason) {
|
||||
switch (failure_reason) {
|
||||
case kInvalidSendReceiveInterval:
|
||||
return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL;
|
||||
case kInvalidSendReceiveRatio:
|
||||
return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO;
|
||||
case kTimeout:
|
||||
return rtclog::BweProbeResult::TIMEOUT;
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
return rtclog::BweProbeResult::SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Checks that the event has a timestamp, a type and exactly the data field
|
||||
// corresponding to the type.
|
||||
::testing::AssertionResult IsValidBasicEvent(const rtclog::Event& event) {
|
||||
if (!event.has_timestamp_us()) {
|
||||
return ::testing::AssertionFailure() << "Event has no timestamp";
|
||||
}
|
||||
if (!event.has_type()) {
|
||||
return ::testing::AssertionFailure() << "Event has no event type";
|
||||
}
|
||||
rtclog::Event_EventType type = event.type();
|
||||
if ((type == rtclog::Event::RTP_EVENT) != event.has_rtp_packet()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_rtp_packet() ? "" : "no ") << "RTP packet";
|
||||
}
|
||||
if ((type == rtclog::Event::RTCP_EVENT) != event.has_rtcp_packet()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_rtcp_packet() ? "" : "no ") << "RTCP packet";
|
||||
}
|
||||
if ((type == rtclog::Event::LOSS_BASED_BWE_UPDATE) !=
|
||||
event.has_loss_based_bwe_update()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_loss_based_bwe_update() ? "" : "no ") << "loss update";
|
||||
}
|
||||
if ((type == rtclog::Event::DELAY_BASED_BWE_UPDATE) !=
|
||||
event.has_delay_based_bwe_update()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_delay_based_bwe_update() ? "" : "no ")
|
||||
<< "delay update";
|
||||
}
|
||||
if ((type == rtclog::Event::AUDIO_PLAYOUT_EVENT) !=
|
||||
event.has_audio_playout_event()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_audio_playout_event() ? "" : "no ")
|
||||
<< "audio_playout event";
|
||||
}
|
||||
if ((type == rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT) !=
|
||||
event.has_video_receiver_config()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_video_receiver_config() ? "" : "no ")
|
||||
<< "receiver config";
|
||||
}
|
||||
if ((type == rtclog::Event::VIDEO_SENDER_CONFIG_EVENT) !=
|
||||
event.has_video_sender_config()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_video_sender_config() ? "" : "no ") << "sender config";
|
||||
}
|
||||
if ((type == rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT) !=
|
||||
event.has_audio_receiver_config()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_audio_receiver_config() ? "" : "no ")
|
||||
<< "audio receiver config";
|
||||
}
|
||||
if ((type == rtclog::Event::AUDIO_SENDER_CONFIG_EVENT) !=
|
||||
event.has_audio_sender_config()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_audio_sender_config() ? "" : "no ")
|
||||
<< "audio sender config";
|
||||
}
|
||||
if ((type == rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT) !=
|
||||
event.has_audio_network_adaptation()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_audio_network_adaptation() ? "" : "no ")
|
||||
<< "audio network adaptation";
|
||||
}
|
||||
if ((type == rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT) !=
|
||||
event.has_probe_cluster()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_probe_cluster() ? "" : "no ") << "bwe probe cluster";
|
||||
}
|
||||
if ((type == rtclog::Event::BWE_PROBE_RESULT_EVENT) !=
|
||||
event.has_probe_result()) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "Event of type " << type << " has "
|
||||
<< (event.has_probe_result() ? "" : "no ") << "bwe probe result";
|
||||
}
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
void VerifyStreamConfigsAreEqual(const rtclog::StreamConfig& config_1,
|
||||
const rtclog::StreamConfig& config_2) {
|
||||
EXPECT_EQ(config_1.remote_ssrc, config_2.remote_ssrc);
|
||||
EXPECT_EQ(config_1.local_ssrc, config_2.local_ssrc);
|
||||
EXPECT_EQ(config_1.rtx_ssrc, config_2.rtx_ssrc);
|
||||
EXPECT_EQ(config_1.rtcp_mode, config_2.rtcp_mode);
|
||||
EXPECT_EQ(config_1.remb, config_2.remb);
|
||||
|
||||
ASSERT_EQ(config_1.rtp_extensions.size(), config_2.rtp_extensions.size());
|
||||
for (size_t i = 0; i < config_2.rtp_extensions.size(); i++) {
|
||||
EXPECT_EQ(config_1.rtp_extensions[i].uri, config_2.rtp_extensions[i].uri);
|
||||
EXPECT_EQ(config_1.rtp_extensions[i].id, config_2.rtp_extensions[i].id);
|
||||
}
|
||||
ASSERT_EQ(config_1.codecs.size(), config_2.codecs.size());
|
||||
for (size_t i = 0; i < config_2.codecs.size(); i++) {
|
||||
EXPECT_EQ(config_1.codecs[i].payload_name, config_2.codecs[i].payload_name);
|
||||
EXPECT_EQ(config_1.codecs[i].payload_type, config_2.codecs[i].payload_type);
|
||||
EXPECT_EQ(config_1.codecs[i].rtx_payload_type,
|
||||
config_2.codecs[i].rtx_payload_type);
|
||||
}
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyVideoReceiveStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT, event.type());
|
||||
const rtclog::VideoReceiveConfig& receiver_config =
|
||||
event.video_receiver_config();
|
||||
// Check SSRCs.
|
||||
ASSERT_TRUE(receiver_config.has_remote_ssrc());
|
||||
EXPECT_EQ(config.remote_ssrc, receiver_config.remote_ssrc());
|
||||
ASSERT_TRUE(receiver_config.has_local_ssrc());
|
||||
EXPECT_EQ(config.local_ssrc, receiver_config.local_ssrc());
|
||||
// Check RTCP settings.
|
||||
ASSERT_TRUE(receiver_config.has_rtcp_mode());
|
||||
if (config.rtcp_mode == RtcpMode::kCompound) {
|
||||
EXPECT_EQ(rtclog::VideoReceiveConfig::RTCP_COMPOUND,
|
||||
receiver_config.rtcp_mode());
|
||||
} else {
|
||||
EXPECT_EQ(rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE,
|
||||
receiver_config.rtcp_mode());
|
||||
}
|
||||
ASSERT_TRUE(receiver_config.has_remb());
|
||||
EXPECT_EQ(config.remb, receiver_config.remb());
|
||||
// Check RTX map.
|
||||
for (const rtclog::RtxMap& rtx_map : receiver_config.rtx_map()) {
|
||||
ASSERT_TRUE(rtx_map.has_payload_type());
|
||||
ASSERT_TRUE(rtx_map.has_config());
|
||||
const rtclog::RtxConfig& rtx_config = rtx_map.config();
|
||||
ASSERT_TRUE(rtx_config.has_rtx_ssrc());
|
||||
ASSERT_TRUE(rtx_config.has_rtx_payload_type());
|
||||
|
||||
EXPECT_EQ(config.rtx_ssrc, rtx_config.rtx_ssrc());
|
||||
auto codec_found =
|
||||
std::find_if(config.codecs.begin(), config.codecs.end(),
|
||||
[&rtx_map](const rtclog::StreamConfig::Codec& codec) {
|
||||
return rtx_map.payload_type() == codec.payload_type;
|
||||
});
|
||||
ASSERT_TRUE(codec_found != config.codecs.end());
|
||||
EXPECT_EQ(rtx_config.rtx_payload_type(), codec_found->rtx_payload_type);
|
||||
}
|
||||
// Check header extensions.
|
||||
ASSERT_EQ(static_cast<int>(config.rtp_extensions.size()),
|
||||
receiver_config.header_extensions_size());
|
||||
for (int i = 0; i < receiver_config.header_extensions_size(); i++) {
|
||||
ASSERT_TRUE(receiver_config.header_extensions(i).has_name());
|
||||
ASSERT_TRUE(receiver_config.header_extensions(i).has_id());
|
||||
const std::string& name = receiver_config.header_extensions(i).name();
|
||||
int id = receiver_config.header_extensions(i).id();
|
||||
EXPECT_EQ(config.rtp_extensions[i].id, id);
|
||||
EXPECT_EQ(config.rtp_extensions[i].uri, name);
|
||||
}
|
||||
// Check decoders.
|
||||
ASSERT_EQ(static_cast<int>(config.codecs.size()),
|
||||
receiver_config.decoders_size());
|
||||
for (int i = 0; i < receiver_config.decoders_size(); i++) {
|
||||
ASSERT_TRUE(receiver_config.decoders(i).has_name());
|
||||
ASSERT_TRUE(receiver_config.decoders(i).has_payload_type());
|
||||
const std::string& decoder_name = receiver_config.decoders(i).name();
|
||||
int decoder_type = receiver_config.decoders(i).payload_type();
|
||||
EXPECT_EQ(config.codecs[i].payload_name, decoder_name);
|
||||
EXPECT_EQ(config.codecs[i].payload_type, decoder_type);
|
||||
}
|
||||
|
||||
// Check consistency of the parser.
|
||||
rtclog::StreamConfig parsed_config = parsed_log.GetVideoReceiveConfig(index);
|
||||
VerifyStreamConfigsAreEqual(config, parsed_config);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyVideoSendStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT, event.type());
|
||||
const rtclog::VideoSendConfig& sender_config = event.video_sender_config();
|
||||
|
||||
EXPECT_EQ(config.local_ssrc, sender_config.ssrcs(0));
|
||||
EXPECT_EQ(config.rtx_ssrc, sender_config.rtx_ssrcs(0));
|
||||
|
||||
// Check header extensions.
|
||||
ASSERT_EQ(static_cast<int>(config.rtp_extensions.size()),
|
||||
sender_config.header_extensions_size());
|
||||
for (int i = 0; i < sender_config.header_extensions_size(); i++) {
|
||||
ASSERT_TRUE(sender_config.header_extensions(i).has_name());
|
||||
ASSERT_TRUE(sender_config.header_extensions(i).has_id());
|
||||
const std::string& name = sender_config.header_extensions(i).name();
|
||||
int id = sender_config.header_extensions(i).id();
|
||||
EXPECT_EQ(config.rtp_extensions[i].id, id);
|
||||
EXPECT_EQ(config.rtp_extensions[i].uri, name);
|
||||
}
|
||||
// Check encoder.
|
||||
ASSERT_TRUE(sender_config.has_encoder());
|
||||
ASSERT_TRUE(sender_config.encoder().has_name());
|
||||
ASSERT_TRUE(sender_config.encoder().has_payload_type());
|
||||
EXPECT_EQ(config.codecs[0].payload_name, sender_config.encoder().name());
|
||||
EXPECT_EQ(config.codecs[0].payload_type,
|
||||
sender_config.encoder().payload_type());
|
||||
|
||||
EXPECT_EQ(config.codecs[0].rtx_payload_type,
|
||||
sender_config.rtx_payload_type());
|
||||
|
||||
// Check consistency of the parser.
|
||||
std::vector<rtclog::StreamConfig> parsed_configs =
|
||||
parsed_log.GetVideoSendConfig(index);
|
||||
ASSERT_EQ(1u, parsed_configs.size());
|
||||
VerifyStreamConfigsAreEqual(config, parsed_configs[0]);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyAudioReceiveStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT, event.type());
|
||||
const rtclog::AudioReceiveConfig& receiver_config =
|
||||
event.audio_receiver_config();
|
||||
// Check SSRCs.
|
||||
ASSERT_TRUE(receiver_config.has_remote_ssrc());
|
||||
EXPECT_EQ(config.remote_ssrc, receiver_config.remote_ssrc());
|
||||
ASSERT_TRUE(receiver_config.has_local_ssrc());
|
||||
EXPECT_EQ(config.local_ssrc, receiver_config.local_ssrc());
|
||||
// Check header extensions.
|
||||
ASSERT_EQ(static_cast<int>(config.rtp_extensions.size()),
|
||||
receiver_config.header_extensions_size());
|
||||
for (int i = 0; i < receiver_config.header_extensions_size(); i++) {
|
||||
ASSERT_TRUE(receiver_config.header_extensions(i).has_name());
|
||||
ASSERT_TRUE(receiver_config.header_extensions(i).has_id());
|
||||
const std::string& name = receiver_config.header_extensions(i).name();
|
||||
int id = receiver_config.header_extensions(i).id();
|
||||
EXPECT_EQ(config.rtp_extensions[i].id, id);
|
||||
EXPECT_EQ(config.rtp_extensions[i].uri, name);
|
||||
}
|
||||
|
||||
// Check consistency of the parser.
|
||||
rtclog::StreamConfig parsed_config = parsed_log.GetAudioReceiveConfig(index);
|
||||
EXPECT_EQ(config.remote_ssrc, parsed_config.remote_ssrc);
|
||||
EXPECT_EQ(config.local_ssrc, parsed_config.local_ssrc);
|
||||
// Check header extensions.
|
||||
EXPECT_EQ(config.rtp_extensions.size(), parsed_config.rtp_extensions.size());
|
||||
for (size_t i = 0; i < parsed_config.rtp_extensions.size(); i++) {
|
||||
EXPECT_EQ(config.rtp_extensions[i].uri,
|
||||
parsed_config.rtp_extensions[i].uri);
|
||||
EXPECT_EQ(config.rtp_extensions[i].id, parsed_config.rtp_extensions[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyAudioSendStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::AUDIO_SENDER_CONFIG_EVENT, event.type());
|
||||
const rtclog::AudioSendConfig& sender_config = event.audio_sender_config();
|
||||
// Check SSRCs.
|
||||
EXPECT_EQ(config.local_ssrc, sender_config.ssrc());
|
||||
// Check header extensions.
|
||||
ASSERT_EQ(static_cast<int>(config.rtp_extensions.size()),
|
||||
sender_config.header_extensions_size());
|
||||
for (int i = 0; i < sender_config.header_extensions_size(); i++) {
|
||||
ASSERT_TRUE(sender_config.header_extensions(i).has_name());
|
||||
ASSERT_TRUE(sender_config.header_extensions(i).has_id());
|
||||
const std::string& name = sender_config.header_extensions(i).name();
|
||||
int id = sender_config.header_extensions(i).id();
|
||||
EXPECT_EQ(config.rtp_extensions[i].id, id);
|
||||
EXPECT_EQ(config.rtp_extensions[i].uri, name);
|
||||
}
|
||||
|
||||
// Check consistency of the parser.
|
||||
rtclog::StreamConfig parsed_config = parsed_log.GetAudioSendConfig(index);
|
||||
VerifyStreamConfigsAreEqual(config, parsed_config);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyRtpEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t header_size,
|
||||
size_t total_size) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::RTP_EVENT, event.type());
|
||||
const rtclog::RtpPacket& rtp_packet = event.rtp_packet();
|
||||
ASSERT_TRUE(rtp_packet.has_incoming());
|
||||
EXPECT_EQ(direction == kIncomingPacket, rtp_packet.incoming());
|
||||
ASSERT_TRUE(rtp_packet.has_packet_length());
|
||||
EXPECT_EQ(total_size, rtp_packet.packet_length());
|
||||
ASSERT_TRUE(rtp_packet.has_header());
|
||||
ASSERT_EQ(header_size, rtp_packet.header().size());
|
||||
for (size_t i = 0; i < header_size; i++) {
|
||||
EXPECT_EQ(header[i], static_cast<uint8_t>(rtp_packet.header()[i]));
|
||||
}
|
||||
|
||||
// Check consistency of the parser.
|
||||
PacketDirection parsed_direction;
|
||||
uint8_t parsed_header[1500];
|
||||
size_t parsed_header_size, parsed_total_size;
|
||||
parsed_log.GetRtpHeader(index, &parsed_direction, parsed_header,
|
||||
&parsed_header_size, &parsed_total_size);
|
||||
EXPECT_EQ(direction, parsed_direction);
|
||||
ASSERT_EQ(header_size, parsed_header_size);
|
||||
EXPECT_EQ(0, std::memcmp(header, parsed_header, header_size));
|
||||
EXPECT_EQ(total_size, parsed_total_size);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyRtcpEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t total_size) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::RTCP_EVENT, event.type());
|
||||
const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet();
|
||||
ASSERT_TRUE(rtcp_packet.has_incoming());
|
||||
EXPECT_EQ(direction == kIncomingPacket, rtcp_packet.incoming());
|
||||
ASSERT_TRUE(rtcp_packet.has_packet_data());
|
||||
ASSERT_EQ(total_size, rtcp_packet.packet_data().size());
|
||||
for (size_t i = 0; i < total_size; i++) {
|
||||
EXPECT_EQ(packet[i], static_cast<uint8_t>(rtcp_packet.packet_data()[i]));
|
||||
}
|
||||
|
||||
// Check consistency of the parser.
|
||||
PacketDirection parsed_direction;
|
||||
uint8_t parsed_packet[1500];
|
||||
size_t parsed_total_size;
|
||||
parsed_log.GetRtcpPacket(index, &parsed_direction, parsed_packet,
|
||||
&parsed_total_size);
|
||||
EXPECT_EQ(direction, parsed_direction);
|
||||
ASSERT_EQ(total_size, parsed_total_size);
|
||||
EXPECT_EQ(0, std::memcmp(packet, parsed_packet, total_size));
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyPlayoutEvent(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t ssrc) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::AUDIO_PLAYOUT_EVENT, event.type());
|
||||
const rtclog::AudioPlayoutEvent& playout_event = event.audio_playout_event();
|
||||
ASSERT_TRUE(playout_event.has_local_ssrc());
|
||||
EXPECT_EQ(ssrc, playout_event.local_ssrc());
|
||||
|
||||
// Check consistency of the parser.
|
||||
uint32_t parsed_ssrc;
|
||||
parsed_log.GetAudioPlayout(index, &parsed_ssrc);
|
||||
EXPECT_EQ(ssrc, parsed_ssrc);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyBweLossEvent(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
int32_t bitrate,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::LOSS_BASED_BWE_UPDATE, event.type());
|
||||
const rtclog::LossBasedBweUpdate& bwe_event = event.loss_based_bwe_update();
|
||||
ASSERT_TRUE(bwe_event.has_bitrate_bps());
|
||||
EXPECT_EQ(bitrate, bwe_event.bitrate_bps());
|
||||
ASSERT_TRUE(bwe_event.has_fraction_loss());
|
||||
EXPECT_EQ(fraction_loss, bwe_event.fraction_loss());
|
||||
ASSERT_TRUE(bwe_event.has_total_packets());
|
||||
EXPECT_EQ(total_packets, bwe_event.total_packets());
|
||||
|
||||
// Check consistency of the parser.
|
||||
int32_t parsed_bitrate;
|
||||
uint8_t parsed_fraction_loss;
|
||||
int32_t parsed_total_packets;
|
||||
parsed_log.GetLossBasedBweUpdate(
|
||||
index, &parsed_bitrate, &parsed_fraction_loss, &parsed_total_packets);
|
||||
EXPECT_EQ(bitrate, parsed_bitrate);
|
||||
EXPECT_EQ(fraction_loss, parsed_fraction_loss);
|
||||
EXPECT_EQ(total_packets, parsed_total_packets);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyBweDelayEvent(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
int32_t bitrate,
|
||||
BandwidthUsage detector_state) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
ASSERT_EQ(rtclog::Event::DELAY_BASED_BWE_UPDATE, event.type());
|
||||
const rtclog::DelayBasedBweUpdate& bwe_event = event.delay_based_bwe_update();
|
||||
ASSERT_TRUE(bwe_event.has_bitrate_bps());
|
||||
EXPECT_EQ(bitrate, bwe_event.bitrate_bps());
|
||||
ASSERT_TRUE(bwe_event.has_detector_state());
|
||||
EXPECT_EQ(detector_state,
|
||||
GetRuntimeDetectorState(bwe_event.detector_state()));
|
||||
|
||||
// Check consistency of the parser.
|
||||
ParsedRtcEventLog::BweDelayBasedUpdate res =
|
||||
parsed_log.GetDelayBasedBweUpdate(index);
|
||||
EXPECT_EQ(res.bitrate_bps, bitrate);
|
||||
EXPECT_EQ(res.detector_state, detector_state);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyAudioNetworkAdaptation(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const AudioEncoderRuntimeConfig& config) {
|
||||
AudioEncoderRuntimeConfig parsed_config;
|
||||
parsed_log.GetAudioNetworkAdaptation(index, &parsed_config);
|
||||
EXPECT_EQ(config.bitrate_bps, parsed_config.bitrate_bps);
|
||||
EXPECT_EQ(config.enable_dtx, parsed_config.enable_dtx);
|
||||
EXPECT_EQ(config.enable_fec, parsed_config.enable_fec);
|
||||
EXPECT_EQ(config.frame_length_ms, parsed_config.frame_length_ms);
|
||||
EXPECT_EQ(config.num_channels, parsed_config.num_channels);
|
||||
EXPECT_EQ(config.uplink_packet_loss_fraction,
|
||||
parsed_config.uplink_packet_loss_fraction);
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyLogStartEvent(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
EXPECT_EQ(rtclog::Event::LOG_START, event.type());
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyLogEndEvent(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
EXPECT_EQ(rtclog::Event::LOG_END, event.type());
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyBweProbeCluster(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
uint32_t bitrate_bps,
|
||||
uint32_t min_probes,
|
||||
uint32_t min_bytes) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
EXPECT_EQ(rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT, event.type());
|
||||
|
||||
const rtclog::BweProbeCluster& bwe_event = event.probe_cluster();
|
||||
ASSERT_TRUE(bwe_event.has_id());
|
||||
EXPECT_EQ(id, bwe_event.id());
|
||||
ASSERT_TRUE(bwe_event.has_bitrate_bps());
|
||||
EXPECT_EQ(bitrate_bps, bwe_event.bitrate_bps());
|
||||
ASSERT_TRUE(bwe_event.has_min_packets());
|
||||
EXPECT_EQ(min_probes, bwe_event.min_packets());
|
||||
ASSERT_TRUE(bwe_event.has_min_bytes());
|
||||
EXPECT_EQ(min_bytes, bwe_event.min_bytes());
|
||||
|
||||
// TODO(philipel): Verify the parser when parsing has been implemented.
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyProbeResultSuccess(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
uint32_t bitrate_bps) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
EXPECT_EQ(rtclog::Event::BWE_PROBE_RESULT_EVENT, event.type());
|
||||
|
||||
const rtclog::BweProbeResult& bwe_event = event.probe_result();
|
||||
ASSERT_TRUE(bwe_event.has_id());
|
||||
EXPECT_EQ(id, bwe_event.id());
|
||||
ASSERT_TRUE(bwe_event.has_bitrate_bps());
|
||||
EXPECT_EQ(bitrate_bps, bwe_event.bitrate_bps());
|
||||
ASSERT_TRUE(bwe_event.has_result());
|
||||
EXPECT_EQ(rtclog::BweProbeResult::SUCCESS, bwe_event.result());
|
||||
|
||||
// TODO(philipel): Verify the parser when parsing has been implemented.
|
||||
}
|
||||
|
||||
void RtcEventLogTestHelper::VerifyProbeResultFailure(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
ProbeFailureReason failure_reason) {
|
||||
const rtclog::Event& event = parsed_log.events_[index];
|
||||
ASSERT_TRUE(IsValidBasicEvent(event));
|
||||
EXPECT_EQ(rtclog::Event::BWE_PROBE_RESULT_EVENT, event.type());
|
||||
|
||||
const rtclog::BweProbeResult& bwe_event = event.probe_result();
|
||||
ASSERT_TRUE(bwe_event.has_id());
|
||||
EXPECT_EQ(id, bwe_event.id());
|
||||
ASSERT_TRUE(bwe_event.has_result());
|
||||
EXPECT_EQ(GetProbeResultType(failure_reason), bwe_event.result());
|
||||
ASSERT_FALSE(bwe_event.has_bitrate_bps());
|
||||
|
||||
// TODO(philipel): Verify the parser when parsing has been implemented.
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
89
logging/rtc_event_log/rtc_event_log_unittest_helper.h
Normal file
89
logging/rtc_event_log/rtc_event_log_unittest_helper.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_
|
||||
|
||||
#include "webrtc/call/call.h"
|
||||
#include "webrtc/logging/rtc_event_log/rtc_event_log_parser.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtcEventLogTestHelper {
|
||||
public:
|
||||
static void VerifyVideoReceiveStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config);
|
||||
static void VerifyVideoSendStreamConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config);
|
||||
static void VerifyAudioReceiveStreamConfig(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config);
|
||||
static void VerifyAudioSendStreamConfig(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const rtclog::StreamConfig& config);
|
||||
static void VerifyRtpEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
PacketDirection direction,
|
||||
const uint8_t* header,
|
||||
size_t header_size,
|
||||
size_t total_size);
|
||||
static void VerifyRtcpEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
PacketDirection direction,
|
||||
const uint8_t* packet,
|
||||
size_t total_size);
|
||||
static void VerifyPlayoutEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t ssrc);
|
||||
static void VerifyBweLossEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
int32_t bitrate,
|
||||
uint8_t fraction_loss,
|
||||
int32_t total_packets);
|
||||
static void VerifyBweDelayEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
int32_t bitrate,
|
||||
BandwidthUsage detector_state);
|
||||
|
||||
static void VerifyAudioNetworkAdaptation(
|
||||
const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
const AudioEncoderRuntimeConfig& config);
|
||||
|
||||
static void VerifyLogStartEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index);
|
||||
static void VerifyLogEndEvent(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index);
|
||||
|
||||
static void VerifyBweProbeCluster(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
uint32_t bitrate_bps,
|
||||
uint32_t min_probes,
|
||||
uint32_t min_bytes);
|
||||
|
||||
static void VerifyProbeResultSuccess(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
uint32_t bitrate_bps);
|
||||
|
||||
static void VerifyProbeResultFailure(const ParsedRtcEventLog& parsed_log,
|
||||
size_t index,
|
||||
uint32_t id,
|
||||
ProbeFailureReason failure_reason);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_
|
||||
29
logging/rtc_event_log/rtc_stream_config.cc
Normal file
29
logging/rtc_event_log/rtc_stream_config.cc
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 "webrtc/logging/rtc_event_log/rtc_stream_config.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace rtclog {
|
||||
|
||||
StreamConfig::StreamConfig() {}
|
||||
|
||||
StreamConfig::~StreamConfig() {}
|
||||
|
||||
StreamConfig::Codec::Codec(const std::string& payload_name,
|
||||
int payload_type,
|
||||
int rtx_payload_type)
|
||||
: payload_name(payload_name),
|
||||
payload_type(payload_type),
|
||||
rtx_payload_type(rtx_payload_type) {}
|
||||
|
||||
|
||||
} // namespace rtclog
|
||||
} // namespace webrtc
|
||||
53
logging/rtc_event_log/rtc_stream_config.h
Normal file
53
logging/rtc_event_log/rtc_stream_config.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_
|
||||
#define WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/api/rtpparameters.h"
|
||||
#include "webrtc/common_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace rtclog {
|
||||
|
||||
struct StreamConfig {
|
||||
StreamConfig();
|
||||
~StreamConfig();
|
||||
|
||||
uint32_t local_ssrc = 0;
|
||||
uint32_t remote_ssrc = 0;
|
||||
uint32_t rtx_ssrc = 0;
|
||||
std::string rsid;
|
||||
|
||||
bool remb = false;
|
||||
std::vector<RtpExtension> rtp_extensions;
|
||||
|
||||
RtcpMode rtcp_mode = RtcpMode::kReducedSize;
|
||||
|
||||
struct Codec {
|
||||
Codec(const std::string& payload_name,
|
||||
int payload_type,
|
||||
int rtx_payload_type);
|
||||
|
||||
std::string payload_name;
|
||||
int payload_type;
|
||||
int rtx_payload_type;
|
||||
};
|
||||
|
||||
std::vector<Codec> codecs;
|
||||
};
|
||||
|
||||
} // namespace rtclog
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_
|
||||
Reference in New Issue
Block a user