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:
Mirko Bonadei
2017-09-15 06:15:48 +02:00
committed by Commit Bot
parent 6674846b4a
commit bb547203bf
4576 changed files with 1092 additions and 1196 deletions

196
logging/BUILD.gn Normal file
View 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
View File

@ -0,0 +1,3 @@
skvlad@webrtc.org
stefan@webrtc.org
terelius@webrtc.org

View 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",
]

View 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_

View 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

View 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_

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View 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_

View 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_

View 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

View 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_

View 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

View 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

View 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_

View 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

View 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_