New parser for event log. Manually parse the outermost EventStream to more easily deal with corrupt or partially written logs.

Changed rtpdump converter and neteq tool to use new parser, but still aborting if the file is corrupt.

Review-Url: https://codereview.webrtc.org/1768773002
Cr-Commit-Position: refs/heads/master@{#12714}
This commit is contained in:
terelius 2016-05-13 00:42:59 -07:00 committed by Commit bot
parent 5df729489f
commit d5c1a0bd5d
12 changed files with 1225 additions and 476 deletions

View File

@ -282,6 +282,29 @@ source_set("rtc_event_log") {
}
}
if (rtc_enable_protobuf) {
source_set("rtc_event_log_parser") {
sources = [
"call/rtc_event_log_parser.cc",
"call/rtc_event_log_parser.h",
]
configs += [ ":common_config" ]
public_configs = [ ":common_inherited_config" ]
deps = [
":rtc_event_log_proto",
":webrtc_common",
]
if (is_clang && !is_nacl) {
# Suppress warnings from Chrome's Clang plugins.
# See http://code.google.com/p/webrtc/issues/detail?id=163 for details.
configs -= [ "//build/config/clang:find_bad_constructs" ]
}
}
}
if (use_libfuzzer || use_drfuzz) {
# This target is only here for gn to discover fuzzer build targets under
# webrtc/test/fuzzers/.

View File

@ -15,17 +15,12 @@
#include "gflags/gflags.h"
#include "webrtc/base/checks.h"
#include "webrtc/call.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/call/rtc_event_log_parser.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/test/rtp_file_writer.h"
// Files generated at build-time by the protobuf compiler.
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/call/rtc_event_log.pb.h"
#else
#include "webrtc/call/rtc_event_log.pb.h"
#endif
namespace {
DEFINE_bool(noaudio,
@ -94,8 +89,8 @@ int main(int argc, char* argv[]) {
RTC_CHECK(ParseSsrc(FLAGS_ssrc, &ssrc_filter))
<< "Flag verification has failed.";
webrtc::rtclog::EventStream event_stream;
if (!webrtc::RtcEventLog::ParseRtcEventLog(input_file, &event_stream)) {
webrtc::ParsedRtcEventLog parsed_stream;
if (!parsed_stream.ParseFile(input_file)) {
std::cerr << "Error while parsing input file: " << input_file << std::endl;
return -1;
}
@ -110,94 +105,78 @@ int main(int argc, char* argv[]) {
return -1;
}
std::cout << "Found " << event_stream.stream_size()
std::cout << "Found " << parsed_stream.GetNumberOfEvents()
<< " events in the input file." << std::endl;
int rtp_counter = 0, rtcp_counter = 0;
bool header_only = false;
// TODO(ivoc): This can be refactored once the packet interpretation
// functions are finished.
for (int i = 0; i < event_stream.stream_size(); i++) {
const webrtc::rtclog::Event& event = event_stream.stream(i);
if (!FLAGS_nortp && event.has_type() && event.type() == event.RTP_EVENT) {
if (event.has_timestamp_us() && event.has_rtp_packet() &&
event.rtp_packet().has_header() &&
event.rtp_packet().header().size() >= 12 &&
event.rtp_packet().has_packet_length() &&
event.rtp_packet().has_type()) {
const webrtc::rtclog::RtpPacket& rtp_packet = event.rtp_packet();
if (FLAGS_noaudio && rtp_packet.type() == webrtc::rtclog::AUDIO)
continue;
if (FLAGS_novideo && rtp_packet.type() == webrtc::rtclog::VIDEO)
continue;
if (FLAGS_nodata && rtp_packet.type() == webrtc::rtclog::DATA)
continue;
if (!FLAGS_ssrc.empty()) {
const uint32_t packet_ssrc =
webrtc::ByteReader<uint32_t>::ReadBigEndian(
reinterpret_cast<const uint8_t*>(rtp_packet.header().data() +
8));
if (packet_ssrc != ssrc_filter)
continue;
}
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 (!FLAGS_nortp &&
parsed_stream.GetEventType(i) == webrtc::ParsedRtcEventLog::RTP_EVENT) {
webrtc::test::RtpPacket packet;
webrtc::PacketDirection direction;
webrtc::MediaType media_type;
parsed_stream.GetRtpHeader(i, &direction, &media_type, 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::test::RtpPacket packet;
packet.length = rtp_packet.header().size();
if (packet.length > packet.kMaxPacketBufferSize) {
std::cout << "Skipping packet with size " << packet.length
<< ", the maximum supported size is "
<< packet.kMaxPacketBufferSize << std::endl;
// TODO(terelius): Maybe add a flag to dump outgoing traffic instead?
if (direction == webrtc::kOutgoingPacket)
continue;
if (FLAGS_noaudio && media_type == webrtc::MediaType::AUDIO)
continue;
if (FLAGS_novideo && media_type == webrtc::MediaType::VIDEO)
continue;
if (FLAGS_nodata && media_type == webrtc::MediaType::DATA)
continue;
if (!FLAGS_ssrc.empty()) {
const uint32_t packet_ssrc =
webrtc::ByteReader<uint32_t>::ReadBigEndian(
reinterpret_cast<const uint8_t*>(packet.data + 8));
if (packet_ssrc != ssrc_filter)
continue;
}
packet.original_length = rtp_packet.packet_length();
if (packet.original_length > packet.length)
header_only = true;
packet.time_ms = event.timestamp_us() / 1000;
memcpy(packet.data, rtp_packet.header().data(), packet.length);
rtp_writer->WritePacket(&packet);
rtp_counter++;
} else {
std::cout << "Skipping malformed event." << std::endl;
}
rtp_writer->WritePacket(&packet);
rtp_counter++;
}
if (!FLAGS_nortcp && event.has_type() && event.type() == event.RTCP_EVENT) {
if (event.has_timestamp_us() && event.has_rtcp_packet() &&
event.rtcp_packet().has_type() &&
event.rtcp_packet().has_packet_data() &&
event.rtcp_packet().packet_data().size() > 0) {
const webrtc::rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet();
if (FLAGS_noaudio && rtcp_packet.type() == webrtc::rtclog::AUDIO)
continue;
if (FLAGS_novideo && rtcp_packet.type() == webrtc::rtclog::VIDEO)
continue;
if (FLAGS_nodata && rtcp_packet.type() == webrtc::rtclog::DATA)
continue;
if (!FLAGS_ssrc.empty()) {
const uint32_t packet_ssrc =
webrtc::ByteReader<uint32_t>::ReadBigEndian(
reinterpret_cast<const uint8_t*>(
rtcp_packet.packet_data().data() + 4));
if (packet_ssrc != ssrc_filter)
continue;
}
if (!FLAGS_nortcp &&
parsed_stream.GetEventType(i) ==
webrtc::ParsedRtcEventLog::RTCP_EVENT) {
webrtc::test::RtpPacket packet;
webrtc::PacketDirection direction;
webrtc::MediaType media_type;
parsed_stream.GetRtcpPacket(i, &direction, &media_type, 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;
webrtc::test::RtpPacket packet;
packet.length = rtcp_packet.packet_data().size();
if (packet.length > packet.kMaxPacketBufferSize) {
std::cout << "Skipping packet with size " << packet.length
<< ", the maximum supported size is "
<< packet.kMaxPacketBufferSize << std::endl;
// TODO(terelius): Maybe add a flag to dump outgoing traffic instead?
if (direction == webrtc::kOutgoingPacket)
continue;
if (FLAGS_noaudio && media_type == webrtc::MediaType::AUDIO)
continue;
if (FLAGS_novideo && media_type == webrtc::MediaType::VIDEO)
continue;
if (FLAGS_nodata && media_type == webrtc::MediaType::DATA)
continue;
if (!FLAGS_ssrc.empty()) {
const uint32_t packet_ssrc =
webrtc::ByteReader<uint32_t>::ReadBigEndian(
reinterpret_cast<const uint8_t*>(packet.data + 4));
if (packet_ssrc != ssrc_filter)
continue;
}
// For RTCP packets the original_length should be set to 0 in the
// RTPdump format.
packet.original_length = 0;
packet.time_ms = event.timestamp_us() / 1000;
memcpy(packet.data, rtcp_packet.packet_data().data(), packet.length);
rtp_writer->WritePacket(&packet);
rtcp_counter++;
} else {
std::cout << "Skipping malformed event." << std::endl;
}
rtp_writer->WritePacket(&packet);
rtcp_counter++;
}
}
std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "")

View File

@ -0,0 +1,395 @@
/*
* 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/call/rtc_event_log_parser.h"
#include <string.h>
#include <fstream>
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/call.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/system_wrappers/include/file_wrapper.h"
namespace webrtc {
namespace {
MediaType GetRuntimeMediaType(rtclog::MediaType media_type) {
switch (media_type) {
case rtclog::MediaType::ANY:
return MediaType::ANY;
case rtclog::MediaType::AUDIO:
return MediaType::AUDIO;
case rtclog::MediaType::VIDEO:
return MediaType::VIDEO;
case rtclog::MediaType::DATA:
return MediaType::DATA;
}
RTC_NOTREACHED();
return MediaType::ANY;
}
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::BWE_PACKET_LOSS_EVENT:
return ParsedRtcEventLog::EventType::BWE_PACKET_LOSS_EVENT;
case rtclog::Event::BWE_PACKET_DELAY_EVENT:
return ParsedRtcEventLog::EventType::BWE_PACKET_DELAY_EVENT;
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;
}
RTC_NOTREACHED();
return ParsedRtcEventLog::EventType::UNKNOWN_EVENT;
}
bool ParseVarInt(std::FILE* file, uint64_t* varint, size_t* bytes_read) {
uint8_t byte;
*varint = 0;
for (*bytes_read = 0; *bytes_read < 10 && fread(&byte, 1, 1, file) == 1;
++(*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.
*varint |= static_cast<uint64_t>(byte & 0x7F) << (7 * *bytes_read);
if ((byte & 0x80) == 0) {
return true;
}
}
return false;
}
} // namespace
bool ParsedRtcEventLog::ParseFile(const std::string& filename) {
stream_.clear();
const size_t kMaxEventSize = (1u << 16) - 1;
char tmp_buffer[kMaxEventSize];
std::FILE* file = fopen(filename.c_str(), "rb");
if (!file) {
LOG(LS_WARNING) << "Could not open file for reading.";
return false;
}
while (1) {
// Peek at 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;
uint64_t tag;
size_t bytes_read;
if (!ParseVarInt(file, &tag, &bytes_read) || tag != kExpectedTag) {
fclose(file);
if (bytes_read == 0) {
return true; // Reached end of file.
}
LOG(LS_WARNING) << "Missing field tag from beginning of protobuf event.";
return false;
}
// Peek at the length field.
uint64_t message_length;
if (!ParseVarInt(file, &message_length, &bytes_read)) {
LOG(LS_WARNING) << "Missing message length after protobuf field tag.";
fclose(file);
return false;
} else if (message_length > kMaxEventSize) {
LOG(LS_WARNING) << "Protobuf message length is too large.";
fclose(file);
return false;
}
if (fread(tmp_buffer, 1, message_length, file) != message_length) {
LOG(LS_WARNING) << "Failed to read protobuf message from file.";
fclose(file);
return false;
}
rtclog::Event event;
if (!event.ParseFromArray(tmp_buffer, message_length)) {
LOG(LS_WARNING) << "Failed to parse protobuf message.";
fclose(file);
return false;
}
stream_.push_back(event);
}
}
size_t ParsedRtcEventLog::GetNumberOfEvents() const {
return stream_.size();
}
int64_t ParsedRtcEventLog::GetTimestamp(size_t index) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[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 = stream_[index];
RTC_CHECK(event.has_type());
return GetRuntimeEventType(event.type());
}
// The header must have space for at least IP_PACKET_SIZE bytes.
void ParsedRtcEventLog::GetRtpHeader(size_t index,
PacketDirection* incoming,
MediaType* media_type,
uint8_t* header,
size_t* header_length,
size_t* total_length) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[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 media type.
RTC_CHECK(rtp_packet.has_type());
if (media_type != nullptr) {
*media_type = GetRuntimeMediaType(rtp_packet.type());
}
// 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());
}
}
// The packet must have space for at least IP_PACKET_SIZE bytes.
void ParsedRtcEventLog::GetRtcpPacket(size_t index,
PacketDirection* incoming,
MediaType* media_type,
uint8_t* packet,
size_t* length) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[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 media type.
RTC_CHECK(rtcp_packet.has_type());
if (media_type != nullptr) {
*media_type = GetRuntimeMediaType(rtcp_packet.type());
}
// 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());
}
}
void ParsedRtcEventLog::GetVideoReceiveConfig(
size_t index,
VideoReceiveStream::Config* config) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[index];
RTC_CHECK(config != nullptr);
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->rtp.remote_ssrc = receiver_config.remote_ssrc();
RTC_CHECK(receiver_config.has_local_ssrc());
config->rtp.local_ssrc = receiver_config.local_ssrc();
// Get RTCP settings.
RTC_CHECK(receiver_config.has_rtcp_mode());
config->rtp.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode());
RTC_CHECK(receiver_config.has_remb());
config->rtp.remb = receiver_config.remb();
// Get RTX map.
config->rtp.rtx.clear();
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());
webrtc::VideoReceiveStream::Config::Rtp::Rtx rtx_pair;
rtx_pair.ssrc = map.config().rtx_ssrc();
rtx_pair.payload_type = map.config().rtx_payload_type();
config->rtp.rtx.insert(std::make_pair(map.payload_type(), rtx_pair));
}
// Get header extensions.
config->rtp.extensions.clear();
for (int i = 0; i < receiver_config.header_extensions_size(); i++) {
RTC_CHECK(receiver_config.header_extensions(i).has_name());
RTC_CHECK(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();
config->rtp.extensions.push_back(RtpExtension(name, id));
}
// Get decoders.
config->decoders.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());
VideoReceiveStream::Decoder decoder;
decoder.payload_name = receiver_config.decoders(i).name();
decoder.payload_type = receiver_config.decoders(i).payload_type();
config->decoders.push_back(decoder);
}
}
void ParsedRtcEventLog::GetVideoSendConfig(
size_t index,
VideoSendStream::Config* config) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[index];
RTC_CHECK(config != nullptr);
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();
// Get SSRCs.
config->rtp.ssrcs.clear();
for (int i = 0; i < sender_config.ssrcs_size(); i++) {
config->rtp.ssrcs.push_back(sender_config.ssrcs(i));
}
// Get header extensions.
config->rtp.extensions.clear();
for (int i = 0; i < sender_config.header_extensions_size(); i++) {
RTC_CHECK(sender_config.header_extensions(i).has_name());
RTC_CHECK(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();
config->rtp.extensions.push_back(RtpExtension(name, id));
}
// Get RTX settings.
config->rtp.rtx.ssrcs.clear();
for (int i = 0; i < sender_config.rtx_ssrcs_size(); i++) {
config->rtp.rtx.ssrcs.push_back(sender_config.rtx_ssrcs(i));
}
if (sender_config.rtx_ssrcs_size() > 0) {
RTC_CHECK(sender_config.has_rtx_payload_type());
config->rtp.rtx.payload_type = sender_config.rtx_payload_type();
} else {
// Reset RTX payload type default value if no RTX SSRCs are used.
config->rtp.rtx.payload_type = -1;
}
// Get encoder.
RTC_CHECK(sender_config.has_encoder());
RTC_CHECK(sender_config.encoder().has_name());
RTC_CHECK(sender_config.encoder().has_payload_type());
config->encoder_settings.payload_name = sender_config.encoder().name();
config->encoder_settings.payload_type =
sender_config.encoder().payload_type();
}
void ParsedRtcEventLog::GetAudioPlayout(size_t index, uint32_t* ssrc) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[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::GetBwePacketLossEvent(size_t index,
int32_t* bitrate,
uint8_t* fraction_loss,
int32_t* total_packets) const {
RTC_CHECK_LT(index, GetNumberOfEvents());
const rtclog::Event& event = stream_[index];
RTC_CHECK(event.has_type());
RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PACKET_LOSS_EVENT);
RTC_CHECK(event.has_bwe_packet_loss_event());
const rtclog::BwePacketLossEvent& loss_event = event.bwe_packet_loss_event();
RTC_CHECK(loss_event.has_bitrate());
if (bitrate != nullptr) {
*bitrate = loss_event.bitrate();
}
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();
}
}
} // namespace webrtc

View File

@ -0,0 +1,114 @@
/*
* 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_CALL_RTC_EVENT_LOG_PARSER_H_
#define WEBRTC_CALL_RTC_EVENT_LOG_PARSER_H_
#include <string>
#include <vector>
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/video_receive_stream.h"
#include "webrtc/video_send_stream.h"
// Files generated at build-time by the protobuf compiler.
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/call/rtc_event_log.pb.h"
#else
#include "webrtc/call/rtc_event_log.pb.h"
#endif
namespace webrtc {
enum class MediaType;
class ParsedRtcEventLog {
friend class RtcEventLogTestHelper;
public:
enum EventType {
UNKNOWN_EVENT = 0,
LOG_START = 1,
LOG_END = 2,
RTP_EVENT = 3,
RTCP_EVENT = 4,
AUDIO_PLAYOUT_EVENT = 5,
BWE_PACKET_LOSS_EVENT = 6,
BWE_PACKET_DELAY_EVENT = 7,
VIDEO_RECEIVER_CONFIG_EVENT = 8,
VIDEO_SENDER_CONFIG_EVENT = 9,
AUDIO_RECEIVER_CONFIG_EVENT = 10,
AUDIO_SENDER_CONFIG_EVENT = 11
};
// Reads an RtcEventLog file and returns true if parsing was successful.
bool ParseFile(const std::string& file_name);
// 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, media type, header length and packet length
// from the RTP event at |index|, and stores the values in the corresponding
// output parameters. The output parameters can be set to nullptr if those
// values aren't needed.
// NB: The header must have space for at least IP_PACKET_SIZE bytes.
void GetRtpHeader(size_t index,
PacketDirection* incoming,
MediaType* media_type,
uint8_t* header,
size_t* header_length,
size_t* total_length) const;
// Reads packet, direction, media type and packet length from the RTCP event
// at |index|, and stores the values in the corresponding output parameters.
// The output parameters can be set to nullptr if those values aren't needed.
// NB: The packet must have space for at least IP_PACKET_SIZE bytes.
void GetRtcpPacket(size_t index,
PacketDirection* incoming,
MediaType* media_type,
uint8_t* packet,
size_t* length) const;
// Reads a config event to a (non-NULL) VideoReceiveStream::Config struct.
// Only the fields that are stored in the protobuf will be written.
void GetVideoReceiveConfig(size_t index,
VideoReceiveStream::Config* config) const;
// Reads a config event to a (non-NULL) VideoSendStream::Config struct.
// Only the fields that are stored in the protobuf will be written.
void GetVideoSendConfig(size_t index, VideoSendStream::Config* config) 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 BWE event at |index| and stores the values in
// the corresponding output parameters. The output parameters can be set to
// nullptr if those values aren't needed.
// NB: The packet must have space for at least IP_PACKET_SIZE bytes.
void GetBwePacketLossEvent(size_t index,
int32_t* bitrate,
uint8_t* fraction_loss,
int32_t* total_packets) const;
private:
std::vector<rtclog::Event> stream_;
};
} // namespace webrtc
#endif // WEBRTC_CALL_RTC_EVENT_LOG_PARSER_H_

View File

@ -22,6 +22,8 @@
#include "webrtc/base/random.h"
#include "webrtc/call.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/call/rtc_event_log_parser.h"
#include "webrtc/call/rtc_event_log_unittest_helper.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_sender.h"
@ -53,250 +55,51 @@ const char* kExtensionNames[] = {RtpExtension::kTOffset,
RtpExtension::kTransportSequenceNumber};
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
// TODO(terelius): Place this definition with other parsing functions?
MediaType GetRuntimeMediaType(rtclog::MediaType media_type) {
switch (media_type) {
case rtclog::MediaType::ANY:
return MediaType::ANY;
case rtclog::MediaType::AUDIO:
return MediaType::AUDIO;
case rtclog::MediaType::VIDEO:
return MediaType::VIDEO;
case rtclog::MediaType::DATA:
return MediaType::DATA;
}
RTC_NOTREACHED();
return MediaType::ANY;
}
// 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::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";
}
return ::testing::AssertionSuccess();
}
void VerifyReceiveStreamConfig(const rtclog::Event& event,
const VideoReceiveStream::Config& config) {
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.rtp.remote_ssrc, receiver_config.remote_ssrc());
ASSERT_TRUE(receiver_config.has_local_ssrc());
EXPECT_EQ(config.rtp.local_ssrc, receiver_config.local_ssrc());
// Check RTCP settings.
ASSERT_TRUE(receiver_config.has_rtcp_mode());
if (config.rtp.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.rtp.remb, receiver_config.remb());
// Check RTX map.
ASSERT_EQ(static_cast<int>(config.rtp.rtx.size()),
receiver_config.rtx_map_size());
for (const rtclog::RtxMap& rtx_map : receiver_config.rtx_map()) {
ASSERT_TRUE(rtx_map.has_payload_type());
ASSERT_TRUE(rtx_map.has_config());
EXPECT_EQ(1u, config.rtp.rtx.count(rtx_map.payload_type()));
const rtclog::RtxConfig& rtx_config = rtx_map.config();
const VideoReceiveStream::Config::Rtp::Rtx& rtx =
config.rtp.rtx.at(rtx_map.payload_type());
ASSERT_TRUE(rtx_config.has_rtx_ssrc());
ASSERT_TRUE(rtx_config.has_rtx_payload_type());
EXPECT_EQ(rtx.ssrc, rtx_config.rtx_ssrc());
EXPECT_EQ(rtx.payload_type, rtx_config.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].name, name);
}
// Check decoders.
ASSERT_EQ(static_cast<int>(config.decoders.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.decoders[i].payload_name, decoder_name);
EXPECT_EQ(config.decoders[i].payload_type, decoder_type);
}
}
void VerifySendStreamConfig(const rtclog::Event& event,
const VideoSendStream::Config& config) {
ASSERT_TRUE(IsValidBasicEvent(event));
ASSERT_EQ(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT, event.type());
const rtclog::VideoSendConfig& sender_config = event.video_sender_config();
// Check SSRCs.
ASSERT_EQ(static_cast<int>(config.rtp.ssrcs.size()),
sender_config.ssrcs_size());
for (int i = 0; i < sender_config.ssrcs_size(); i++) {
EXPECT_EQ(config.rtp.ssrcs[i], sender_config.ssrcs(i));
}
// 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].name, name);
}
// Check RTX settings.
ASSERT_EQ(static_cast<int>(config.rtp.rtx.ssrcs.size()),
sender_config.rtx_ssrcs_size());
for (int i = 0; i < sender_config.rtx_ssrcs_size(); i++) {
EXPECT_EQ(config.rtp.rtx.ssrcs[i], sender_config.rtx_ssrcs(i));
}
if (sender_config.rtx_ssrcs_size() > 0) {
ASSERT_TRUE(sender_config.has_rtx_payload_type());
EXPECT_EQ(config.rtp.rtx.payload_type, sender_config.rtx_payload_type());
}
// 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.encoder_settings.payload_name,
sender_config.encoder().name());
EXPECT_EQ(config.encoder_settings.payload_type,
sender_config.encoder().payload_type());
}
void VerifyRtpEvent(const rtclog::Event& event,
PacketDirection direction,
MediaType media_type,
const uint8_t* header,
size_t header_size,
size_t total_size) {
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_type());
EXPECT_EQ(media_type, GetRuntimeMediaType(rtp_packet.type()));
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]));
}
}
void VerifyRtcpEvent(const rtclog::Event& event,
PacketDirection direction,
MediaType media_type,
const uint8_t* packet,
size_t total_size) {
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_type());
EXPECT_EQ(media_type, GetRuntimeMediaType(rtcp_packet.type()));
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]));
}
}
void VerifyPlayoutEvent(const rtclog::Event& event, uint32_t ssrc) {
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());
}
void VerifyBweLossEvent(const rtclog::Event& event,
int32_t bitrate,
uint8_t fraction_loss,
int32_t total_packets) {
ASSERT_TRUE(IsValidBasicEvent(event));
ASSERT_EQ(rtclog::Event::BWE_PACKET_LOSS_EVENT, event.type());
const rtclog::BwePacketLossEvent& bwe_event = event.bwe_packet_loss_event();
ASSERT_TRUE(bwe_event.has_bitrate());
EXPECT_EQ(bitrate, bwe_event.bitrate());
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());
}
void VerifyLogStartEvent(const rtclog::Event& event) {
ASSERT_TRUE(IsValidBasicEvent(event));
EXPECT_EQ(rtclog::Event::LOG_START, event.type());
}
void VerifyLogEndEvent(const rtclog::Event& event) {
ASSERT_TRUE(IsValidBasicEvent(event));
EXPECT_EQ(rtclog::Event::LOG_END, event.type());
}
/*
* Bit number i of extension_bitvector is set to indicate the
* presence of extension number i from kExtensionTypes / kExtensionNames.
@ -523,90 +326,58 @@ void LogSessionAndReadBack(size_t rtp_count,
}
// Read the generated file from disk.
rtclog::EventStream parsed_stream;
ParsedRtcEventLog parsed_log;
ASSERT_TRUE(RtcEventLog::ParseRtcEventLog(temp_filename, &parsed_stream));
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 int event_count = config_count + playout_count + bwe_loss_count +
rtcp_count + rtp_count + 2;
EXPECT_GE(1000, event_count); // The events must fit in the message queue.
EXPECT_EQ(event_count, parsed_stream.stream_size());
if (event_count != parsed_stream.stream_size()) {
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.
std::map<int, size_t> actual_event_counts;
for (size_t i = 0; i < static_cast<size_t>(parsed_stream.stream_size());
i++) {
actual_event_counts[parsed_stream.stream(i).type()]++;
}
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 < static_cast<size_t>(parsed_stream.stream_size());
i++) {
printf("%4d ", parsed_stream.stream(i).type());
}
printf("\n");
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("\n");
PrintActualEvents(parsed_log);
PrintExpectedEvents(rtp_count, rtcp_count, playout_count, bwe_loss_count);
}
VerifyLogStartEvent(parsed_stream.stream(0));
VerifyReceiveStreamConfig(parsed_stream.stream(1), receiver_config);
VerifySendStreamConfig(parsed_stream.stream(2), sender_config);
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
RtcEventLogTestHelper::VerifyReceiveStreamConfig(parsed_log, 1,
receiver_config);
RtcEventLogTestHelper::VerifySendStreamConfig(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++) {
VerifyRtpEvent(parsed_stream.stream(event_index),
(i % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
(i % 3 == 0) ? MediaType::AUDIO : MediaType::VIDEO,
rtp_packets[i - 1].data(), rtp_header_sizes[i - 1],
rtp_packets[i - 1].size());
RtcEventLogTestHelper::VerifyRtpEvent(
parsed_log, event_index,
(i % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
(i % 3 == 0) ? MediaType::AUDIO : MediaType::VIDEO,
rtp_packets[i - 1].data(), rtp_header_sizes[i - 1],
rtp_packets[i - 1].size());
event_index++;
if (i * rtcp_count >= rtcp_index * rtp_count) {
VerifyRtcpEvent(parsed_stream.stream(event_index),
(rtcp_index % 2 == 0) ? kIncomingPacket : kOutgoingPacket,
rtcp_index % 3 == 0 ? MediaType::AUDIO : MediaType::VIDEO,
rtcp_packets[rtcp_index - 1].data(),
rtcp_packets[rtcp_index - 1].size());
RtcEventLogTestHelper::VerifyRtcpEvent(
parsed_log, event_index,
rtcp_index % 2 == 0 ? kIncomingPacket : kOutgoingPacket,
rtcp_index % 3 == 0 ? MediaType::AUDIO : MediaType::VIDEO,
rtcp_packets[rtcp_index - 1].data(),
rtcp_packets[rtcp_index - 1].size());
event_index++;
rtcp_index++;
}
if (i * playout_count >= playout_index * rtp_count) {
VerifyPlayoutEvent(parsed_stream.stream(event_index),
playout_ssrcs[playout_index - 1]);
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) {
VerifyBweLossEvent(parsed_stream.stream(event_index),
bwe_loss_updates[bwe_loss_index - 1].first,
bwe_loss_updates[bwe_loss_index - 1].second, i);
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++;
}
@ -683,22 +454,24 @@ TEST(RtcEventLogTest, LogEventAndReadBack) {
log_dumper->StopLogging();
// Read the generated file from disk.
rtclog::EventStream parsed_stream;
ASSERT_TRUE(RtcEventLog::ParseRtcEventLog(temp_filename, &parsed_stream));
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(4, parsed_stream.stream_size());
EXPECT_EQ(4u, parsed_log.GetNumberOfEvents());
VerifyLogStartEvent(parsed_stream.stream(0));
RtcEventLogTestHelper::VerifyLogStartEvent(parsed_log, 0);
VerifyRtpEvent(parsed_stream.stream(1), kIncomingPacket, MediaType::VIDEO,
rtp_packet.data(), header_size, rtp_packet.size());
RtcEventLogTestHelper::VerifyRtpEvent(parsed_log, 1, kIncomingPacket,
MediaType::VIDEO, rtp_packet.data(),
header_size, rtp_packet.size());
VerifyRtcpEvent(parsed_stream.stream(2), kOutgoingPacket, MediaType::VIDEO,
rtcp_packet.data(), rtcp_packet.size());
RtcEventLogTestHelper::VerifyRtcpEvent(parsed_log, 2, kOutgoingPacket,
MediaType::VIDEO, rtcp_packet.data(),
rtcp_packet.size());
VerifyLogEndEvent(parsed_stream.stream(3));
RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, 3);
// Clean up temporary file - can be pretty slow.
remove(temp_filename.c_str());

View File

@ -0,0 +1,409 @@
/*
* 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.
*/
#ifdef ENABLE_RTC_EVENT_LOG
#include "webrtc/call/rtc_event_log_unittest_helper.h"
#include <string.h>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/checks.h"
#include "webrtc/test/test_suite.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/call/rtc_event_log.pb.h"
#else
#include "webrtc/call/rtc_event_log.pb.h"
#endif
namespace webrtc {
namespace {
MediaType GetRuntimeMediaType(rtclog::MediaType media_type) {
switch (media_type) {
case rtclog::MediaType::ANY:
return MediaType::ANY;
case rtclog::MediaType::AUDIO:
return MediaType::AUDIO;
case rtclog::MediaType::VIDEO:
return MediaType::VIDEO;
case rtclog::MediaType::DATA:
return MediaType::DATA;
}
RTC_NOTREACHED();
return MediaType::ANY;
}
} // 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::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";
}
return ::testing::AssertionSuccess();
}
void RtcEventLogTestHelper::VerifyReceiveStreamConfig(
const ParsedRtcEventLog& parsed_log,
size_t index,
const VideoReceiveStream::Config& config) {
const rtclog::Event& event = parsed_log.stream_[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.rtp.remote_ssrc, receiver_config.remote_ssrc());
ASSERT_TRUE(receiver_config.has_local_ssrc());
EXPECT_EQ(config.rtp.local_ssrc, receiver_config.local_ssrc());
// Check RTCP settings.
ASSERT_TRUE(receiver_config.has_rtcp_mode());
if (config.rtp.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.rtp.remb, receiver_config.remb());
// Check RTX map.
ASSERT_EQ(static_cast<int>(config.rtp.rtx.size()),
receiver_config.rtx_map_size());
for (const rtclog::RtxMap& rtx_map : receiver_config.rtx_map()) {
ASSERT_TRUE(rtx_map.has_payload_type());
ASSERT_TRUE(rtx_map.has_config());
EXPECT_EQ(1u, config.rtp.rtx.count(rtx_map.payload_type()));
const rtclog::RtxConfig& rtx_config = rtx_map.config();
const VideoReceiveStream::Config::Rtp::Rtx& rtx =
config.rtp.rtx.at(rtx_map.payload_type());
ASSERT_TRUE(rtx_config.has_rtx_ssrc());
ASSERT_TRUE(rtx_config.has_rtx_payload_type());
EXPECT_EQ(rtx.ssrc, rtx_config.rtx_ssrc());
EXPECT_EQ(rtx.payload_type, rtx_config.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].name, name);
}
// Check decoders.
ASSERT_EQ(static_cast<int>(config.decoders.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.decoders[i].payload_name, decoder_name);
EXPECT_EQ(config.decoders[i].payload_type, decoder_type);
}
// Check consistency of the parser.
VideoReceiveStream::Config parsed_config(nullptr);
parsed_log.GetVideoReceiveConfig(index, &parsed_config);
EXPECT_EQ(config.rtp.remote_ssrc, parsed_config.rtp.remote_ssrc);
EXPECT_EQ(config.rtp.local_ssrc, parsed_config.rtp.local_ssrc);
// Check RTCP settings.
EXPECT_EQ(config.rtp.rtcp_mode, parsed_config.rtp.rtcp_mode);
EXPECT_EQ(config.rtp.remb, parsed_config.rtp.remb);
// Check RTX map.
EXPECT_EQ(config.rtp.rtx.size(), parsed_config.rtp.rtx.size());
for (const auto& kv : config.rtp.rtx) {
auto parsed_kv = parsed_config.rtp.rtx.find(kv.first);
EXPECT_EQ(kv.first, parsed_kv->first);
EXPECT_EQ(kv.second.ssrc, parsed_kv->second.ssrc);
EXPECT_EQ(kv.second.payload_type, parsed_kv->second.payload_type);
}
// 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].name,
parsed_config.rtp.extensions[i].name);
EXPECT_EQ(config.rtp.extensions[i].id, parsed_config.rtp.extensions[i].id);
}
// Check decoders.
EXPECT_EQ(config.decoders.size(), parsed_config.decoders.size());
for (size_t i = 0; i < parsed_config.decoders.size(); i++) {
EXPECT_EQ(config.decoders[i].payload_name,
parsed_config.decoders[i].payload_name);
EXPECT_EQ(config.decoders[i].payload_type,
parsed_config.decoders[i].payload_type);
}
}
void RtcEventLogTestHelper::VerifySendStreamConfig(
const ParsedRtcEventLog& parsed_log,
size_t index,
const VideoSendStream::Config& config) {
const rtclog::Event& event = parsed_log.stream_[index];
ASSERT_TRUE(IsValidBasicEvent(event));
ASSERT_EQ(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT, event.type());
const rtclog::VideoSendConfig& sender_config = event.video_sender_config();
// Check SSRCs.
ASSERT_EQ(static_cast<int>(config.rtp.ssrcs.size()),
sender_config.ssrcs_size());
for (int i = 0; i < sender_config.ssrcs_size(); i++) {
EXPECT_EQ(config.rtp.ssrcs[i], sender_config.ssrcs(i));
}
// 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].name, name);
}
// Check RTX settings.
ASSERT_EQ(static_cast<int>(config.rtp.rtx.ssrcs.size()),
sender_config.rtx_ssrcs_size());
for (int i = 0; i < sender_config.rtx_ssrcs_size(); i++) {
EXPECT_EQ(config.rtp.rtx.ssrcs[i], sender_config.rtx_ssrcs(i));
}
if (sender_config.rtx_ssrcs_size() > 0) {
ASSERT_TRUE(sender_config.has_rtx_payload_type());
EXPECT_EQ(config.rtp.rtx.payload_type, sender_config.rtx_payload_type());
}
// 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.encoder_settings.payload_name,
sender_config.encoder().name());
EXPECT_EQ(config.encoder_settings.payload_type,
sender_config.encoder().payload_type());
// Check consistency of the parser.
VideoSendStream::Config parsed_config(nullptr);
parsed_log.GetVideoSendConfig(index, &parsed_config);
// Check SSRCs
EXPECT_EQ(config.rtp.ssrcs.size(), parsed_config.rtp.ssrcs.size());
for (size_t i = 0; i < config.rtp.ssrcs.size(); i++) {
EXPECT_EQ(config.rtp.ssrcs[i], parsed_config.rtp.ssrcs[i]);
}
// 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].name,
parsed_config.rtp.extensions[i].name);
EXPECT_EQ(config.rtp.extensions[i].id, parsed_config.rtp.extensions[i].id);
}
// Check RTX settings.
EXPECT_EQ(config.rtp.rtx.ssrcs.size(), parsed_config.rtp.rtx.ssrcs.size());
for (size_t i = 0; i < config.rtp.rtx.ssrcs.size(); i++) {
EXPECT_EQ(config.rtp.rtx.ssrcs[i], parsed_config.rtp.rtx.ssrcs[i]);
}
EXPECT_EQ(config.rtp.rtx.payload_type, parsed_config.rtp.rtx.payload_type);
// Check encoder.
EXPECT_EQ(config.encoder_settings.payload_name,
parsed_config.encoder_settings.payload_name);
EXPECT_EQ(config.encoder_settings.payload_type,
parsed_config.encoder_settings.payload_type);
}
void RtcEventLogTestHelper::VerifyRtpEvent(const ParsedRtcEventLog& parsed_log,
size_t index,
PacketDirection direction,
MediaType media_type,
const uint8_t* header,
size_t header_size,
size_t total_size) {
const rtclog::Event& event = parsed_log.stream_[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_type());
EXPECT_EQ(media_type, GetRuntimeMediaType(rtp_packet.type()));
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;
MediaType parsed_media_type;
uint8_t parsed_header[1500];
size_t parsed_header_size, parsed_total_size;
parsed_log.GetRtpHeader(index, &parsed_direction, &parsed_media_type,
parsed_header, &parsed_header_size,
&parsed_total_size);
EXPECT_EQ(direction, parsed_direction);
EXPECT_EQ(media_type, parsed_media_type);
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,
MediaType media_type,
const uint8_t* packet,
size_t total_size) {
const rtclog::Event& event = parsed_log.stream_[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_type());
EXPECT_EQ(media_type, GetRuntimeMediaType(rtcp_packet.type()));
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;
MediaType parsed_media_type;
uint8_t parsed_packet[1500];
size_t parsed_total_size;
parsed_log.GetRtcpPacket(index, &parsed_direction, &parsed_media_type,
parsed_packet, &parsed_total_size);
EXPECT_EQ(direction, parsed_direction);
EXPECT_EQ(media_type, parsed_media_type);
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.stream_[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.stream_[index];
ASSERT_TRUE(IsValidBasicEvent(event));
ASSERT_EQ(rtclog::Event::BWE_PACKET_LOSS_EVENT, event.type());
const rtclog::BwePacketLossEvent& bwe_event = event.bwe_packet_loss_event();
ASSERT_TRUE(bwe_event.has_bitrate());
EXPECT_EQ(bitrate, bwe_event.bitrate());
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.GetBwePacketLossEvent(
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::VerifyLogStartEvent(
const ParsedRtcEventLog& parsed_log,
size_t index) {
const rtclog::Event& event = parsed_log.stream_[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.stream_[index];
ASSERT_TRUE(IsValidBasicEvent(event));
EXPECT_EQ(rtclog::Event::LOG_END, event.type());
}
} // namespace webrtc
#endif // ENABLE_RTC_EVENT_LOG

View File

@ -0,0 +1,58 @@
/*
* 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_CALL_RTC_EVENT_LOG_UNITTEST_HELPER_H_
#define WEBRTC_CALL_RTC_EVENT_LOG_UNITTEST_HELPER_H_
#include "webrtc/call.h"
#include "webrtc/call/rtc_event_log_parser.h"
namespace webrtc {
class RtcEventLogTestHelper {
public:
static void VerifyReceiveStreamConfig(
const ParsedRtcEventLog& parsed_log,
size_t index,
const VideoReceiveStream::Config& config);
static void VerifySendStreamConfig(const ParsedRtcEventLog& parsed_log,
size_t index,
const VideoSendStream::Config& config);
static void VerifyRtpEvent(const ParsedRtcEventLog& parsed_log,
size_t index,
PacketDirection direction,
MediaType media_type,
const uint8_t* header,
size_t header_size,
size_t total_size);
static void VerifyRtcpEvent(const ParsedRtcEventLog& parsed_log,
size_t index,
PacketDirection direction,
MediaType media_type,
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 VerifyLogStartEvent(const ParsedRtcEventLog& parsed_log,
size_t index);
static void VerifyLogEndEvent(const ParsedRtcEventLog& parsed_log,
size_t index);
};
} // namespace webrtc
#endif // WEBRTC_CALL_RTC_EVENT_LOG_UNITTEST_HELPER_H_

View File

@ -14,9 +14,12 @@
'target_name': 'rtc_event_log_source',
'type': 'static_library',
'dependencies': [
'<(webrtc_root)/webrtc.gyp:rtc_event_log',
'<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
'<(webrtc_root)/webrtc.gyp:rtc_event_log_proto',
],
'export_dependent_settings': [
'<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
],
'sources': [
'tools/rtc_event_log_source.h',
'tools/rtc_event_log_source.cc',

View File

@ -16,51 +16,15 @@
#include <limits>
#include "webrtc/base/checks.h"
#include "webrtc/call.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/modules/audio_coding/neteq/tools/packet.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
// Files generated at build-time by the protobuf compiler.
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/call/rtc_event_log.pb.h"
#else
#include "webrtc/call/rtc_event_log.pb.h"
#endif
namespace webrtc {
namespace test {
namespace {
const rtclog::RtpPacket* GetRtpPacket(const rtclog::Event& event) {
if (!event.has_type() || event.type() != rtclog::Event::RTP_EVENT)
return nullptr;
if (!event.has_timestamp_us() || !event.has_rtp_packet())
return nullptr;
const rtclog::RtpPacket& rtp_packet = event.rtp_packet();
if (!rtp_packet.has_type() || rtp_packet.type() != rtclog::AUDIO ||
!rtp_packet.has_incoming() || !rtp_packet.incoming() ||
!rtp_packet.has_packet_length() || rtp_packet.packet_length() == 0 ||
!rtp_packet.has_header() || rtp_packet.header().size() == 0 ||
rtp_packet.packet_length() < rtp_packet.header().size())
return nullptr;
return &rtp_packet;
}
const rtclog::AudioPlayoutEvent* GetAudioPlayoutEvent(
const rtclog::Event& event) {
if (!event.has_type() || event.type() != rtclog::Event::AUDIO_PLAYOUT_EVENT)
return nullptr;
if (!event.has_timestamp_us() || !event.has_audio_playout_event())
return nullptr;
const rtclog::AudioPlayoutEvent& playout_event = event.audio_playout_event();
if (!playout_event.has_local_ssrc())
return nullptr;
return &playout_event;
}
} // namespace
RtcEventLogSource* RtcEventLogSource::Create(const std::string& file_name) {
RtcEventLogSource* source = new RtcEventLogSource();
RTC_CHECK(source->OpenFile(file_name));
@ -76,42 +40,57 @@ bool RtcEventLogSource::RegisterRtpHeaderExtension(RTPExtensionType type,
}
Packet* RtcEventLogSource::NextPacket() {
while (rtp_packet_index_ < event_log_->stream_size()) {
const rtclog::Event& event = event_log_->stream(rtp_packet_index_);
const rtclog::RtpPacket* rtp_packet = GetRtpPacket(event);
rtp_packet_index_++;
if (rtp_packet) {
uint8_t* packet_header = new uint8_t[rtp_packet->header().size()];
memcpy(packet_header, rtp_packet->header().data(),
rtp_packet->header().size());
Packet* packet = new Packet(packet_header, rtp_packet->header().size(),
rtp_packet->packet_length(),
event.timestamp_us() / 1000, *parser_.get());
if (packet->valid_header()) {
// Check if the packet should not be filtered out.
if (!filter_.test(packet->header().payloadType) &&
!(use_ssrc_filter_ && packet->header().ssrc != ssrc_))
return packet;
} else {
std::cout << "Warning: Packet with index " << (rtp_packet_index_ - 1)
<< " has an invalid header and will be ignored." << std::endl;
while (rtp_packet_index_ < parsed_stream_.GetNumberOfEvents()) {
if (parsed_stream_.GetEventType(rtp_packet_index_) ==
ParsedRtcEventLog::RTP_EVENT) {
PacketDirection direction;
MediaType media_type;
size_t header_length;
size_t packet_length;
uint64_t timestamp_us = parsed_stream_.GetTimestamp(rtp_packet_index_);
parsed_stream_.GetRtpHeader(rtp_packet_index_, &direction, &media_type,
nullptr, &header_length, &packet_length);
if (direction == kIncomingPacket && media_type == MediaType::AUDIO) {
uint8_t* packet_header = new uint8_t[header_length];
parsed_stream_.GetRtpHeader(rtp_packet_index_, nullptr, nullptr,
packet_header, nullptr, nullptr);
Packet* packet = new Packet(packet_header, header_length, packet_length,
static_cast<double>(timestamp_us) / 1000,
*parser_.get());
if (packet->valid_header()) {
// Check if the packet should not be filtered out.
if (!filter_.test(packet->header().payloadType) &&
!(use_ssrc_filter_ && packet->header().ssrc != ssrc_)) {
rtp_packet_index_++;
return packet;
}
} else {
std::cout << "Warning: Packet with index " << rtp_packet_index_
<< " has an invalid header and will be ignored."
<< std::endl;
}
// The packet has either an invalid header or needs to be filtered out,
// so it can be deleted.
delete packet;
}
// The packet has either an invalid header or needs to be filtered out, so
// it can be deleted.
delete packet;
}
rtp_packet_index_++;
}
return nullptr;
}
int64_t RtcEventLogSource::NextAudioOutputEventMs() {
while (audio_output_index_ < event_log_->stream_size()) {
const rtclog::Event& event = event_log_->stream(audio_output_index_);
const rtclog::AudioPlayoutEvent* playout_event =
GetAudioPlayoutEvent(event);
while (audio_output_index_ < parsed_stream_.GetNumberOfEvents()) {
if (parsed_stream_.GetEventType(audio_output_index_) ==
ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
uint64_t timestamp_us = parsed_stream_.GetTimestamp(audio_output_index_);
// We call GetAudioPlayout only to check that the protobuf event is
// well-formed.
parsed_stream_.GetAudioPlayout(audio_output_index_, nullptr);
audio_output_index_++;
return timestamp_us / 1000;
}
audio_output_index_++;
if (playout_event)
return event.timestamp_us() / 1000;
}
return std::numeric_limits<int64_t>::max();
}
@ -120,8 +99,7 @@ RtcEventLogSource::RtcEventLogSource()
: PacketSource(), parser_(RtpHeaderParser::Create()) {}
bool RtcEventLogSource::OpenFile(const std::string& file_name) {
event_log_.reset(new rtclog::EventStream());
return RtcEventLog::ParseRtcEventLog(file_name, event_log_.get());
return parsed_stream_.ParseFile(file_name);
}
} // namespace test

View File

@ -15,6 +15,7 @@
#include <string>
#include "webrtc/base/constructormagic.h"
#include "webrtc/call/rtc_event_log_parser.h"
#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@ -22,10 +23,6 @@ namespace webrtc {
class RtpHeaderParser;
namespace rtclog {
class EventStream;
} // namespace rtclog
namespace test {
class Packet;
@ -55,10 +52,10 @@ class RtcEventLogSource : public PacketSource {
bool OpenFile(const std::string& file_name);
int rtp_packet_index_ = 0;
int audio_output_index_ = 0;
size_t rtp_packet_index_ = 0;
size_t audio_output_index_ = 0;
std::unique_ptr<rtclog::EventStream> event_log_;
ParsedRtcEventLog parsed_stream_;
std::unique_ptr<RtpHeaderParser> parser_;
RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogSource);

View File

@ -67,6 +67,24 @@
},
],
}],
['enable_protobuf==1', {
'targets': [
{
'target_name': 'rtc_event_log_parser',
'type': 'static_library',
'sources': [
'call/rtc_event_log_parser.cc',
'call/rtc_event_log_parser.h',
],
'dependencies': [
'rtc_event_log_proto',
],
'export_dependent_settings': [
'rtc_event_log_proto',
],
},
],
}],
['include_tests==1 and enable_protobuf==1', {
'targets': [
{
@ -75,7 +93,7 @@
'sources': ['call/rtc_event_log2rtp_dump.cc',],
'dependencies': [
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'rtc_event_log',
'rtc_event_log_parser',
'rtc_event_log_proto',
'test/test.gyp:rtp_test_utils'
],

View File

@ -213,10 +213,12 @@
],
'dependencies': [
'webrtc.gyp:rtc_event_log',
'webrtc.gyp:rtc_event_log_parser',
'webrtc.gyp:rtc_event_log_proto',
],
'sources': [
'call/rtc_event_log_unittest.cc',
'call/rtc_event_log_unittest_helper.cc'
],
}],
],