blob: 9a8905d402d8164590547ab49bac2f59e82b346d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cast/logging/encoding_event_subscriber.h"
#include <stdint.h>
#include <algorithm>
#include <cstring>
#include <utility>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "media/cast/logging/proto/proto_utils.h"
using google::protobuf::RepeatedPtrField;
using media::cast::proto::AggregatedFrameEvent;
using media::cast::proto::AggregatedPacketEvent;
using media::cast::proto::BasePacketEvent;
using media::cast::proto::LogMetadata;
namespace {
// A size limit on maps to keep lookups fast.
const size_t kMaxMapSize = 200;
// The smallest (oredered by RTP timestamp) |kNumMapEntriesToTransfer| entries
// will be moved when the map size reaches |kMaxMapSize|.
// Must be smaller than |kMaxMapSize|.
const size_t kNumMapEntriesToTransfer = 100;
template <typename ProtoPtr>
bool IsRtpTimestampLessThan(const ProtoPtr& lhs, const ProtoPtr& rhs) {
return lhs->relative_rtp_timestamp() < rhs->relative_rtp_timestamp();
}
BasePacketEvent* GetNewBasePacketEvent(AggregatedPacketEvent* event_proto,
int packet_id, int size) {
BasePacketEvent* base = event_proto->add_base_packet_event();
base->set_packet_id(packet_id);
base->set_size(size);
return base;
}
} // namespace
namespace media {
namespace cast {
EncodingEventSubscriber::EncodingEventSubscriber(
EventMediaType event_media_type,
size_t max_frames)
: event_media_type_(event_media_type), max_frames_(max_frames) {
Reset();
}
EncodingEventSubscriber::~EncodingEventSubscriber() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void EncodingEventSubscriber::OnReceiveFrameEvent(
const FrameEvent& frame_event) {
DCHECK(thread_checker_.CalledOnValidThread());
if (event_media_type_ != frame_event.media_type)
return;
const RtpTimeDelta relative_rtp_timestamp =
GetRelativeRtpTimestamp(frame_event.rtp_timestamp);
const uint32_t lower_32_bits = relative_rtp_timestamp.lower_32_bits();
AggregatedFrameEvent* event_proto_ptr = nullptr;
// Look up existing entry. If not found, create a new entry and add to map.
auto it = frame_event_map_.find(relative_rtp_timestamp);
if (it == frame_event_map_.end()) {
if (!ShouldCreateNewProto(lower_32_bits))
return;
IncrementStoredProtoCount(lower_32_bits);
auto event_proto = std::make_unique<AggregatedFrameEvent>();
event_proto->set_relative_rtp_timestamp(lower_32_bits);
event_proto_ptr = event_proto.get();
frame_event_map_.insert(
std::make_pair(relative_rtp_timestamp, std::move(event_proto)));
} else {
if (it->second->event_type_size() >= kMaxEventsPerProto) {
DVLOG(2) << "Too many events in frame " << frame_event.rtp_timestamp
<< ". Using new frame event proto.";
AddFrameEventToStorage(std::move(it->second));
if (!ShouldCreateNewProto(lower_32_bits)) {
frame_event_map_.erase(it);
return;
}
IncrementStoredProtoCount(lower_32_bits);
it->second = std::make_unique<AggregatedFrameEvent>();
it->second->set_relative_rtp_timestamp(lower_32_bits);
}
event_proto_ptr = it->second.get();
}
event_proto_ptr->add_event_type(ToProtoEventType(frame_event.type));
event_proto_ptr->add_event_timestamp_ms(
(frame_event.timestamp - base::TimeTicks()).InMilliseconds());
if (frame_event.type == FRAME_CAPTURE_END) {
if (frame_event.media_type == VIDEO_EVENT &&
frame_event.width > 0 && frame_event.height > 0) {
event_proto_ptr->set_width(frame_event.width);
event_proto_ptr->set_height(frame_event.height);
}
} else if (frame_event.type == FRAME_ENCODED) {
event_proto_ptr->set_encoded_frame_size(frame_event.size);
if (frame_event.encoder_cpu_utilization >= 0.0) {
event_proto_ptr->set_encoder_cpu_percent_utilized(
base::saturated_cast<int32_t>(
frame_event.encoder_cpu_utilization * 100.0 + 0.5));
}
if (frame_event.idealized_bitrate_utilization >= 0.0) {
event_proto_ptr->set_idealized_bitrate_percent_utilized(
base::saturated_cast<int32_t>(
frame_event.idealized_bitrate_utilization * 100.0 + 0.5));
}
if (frame_event.media_type == VIDEO_EVENT) {
event_proto_ptr->set_key_frame(frame_event.key_frame);
event_proto_ptr->set_target_bitrate(frame_event.target_bitrate);
}
} else if (frame_event.type == FRAME_PLAYOUT) {
event_proto_ptr->set_delay_millis(frame_event.delay_delta.InMilliseconds());
}
if (frame_event_map_.size() > kMaxMapSize)
TransferFrameEvents(kNumMapEntriesToTransfer);
DCHECK(frame_event_map_.size() <= kMaxMapSize);
DCHECK(frame_event_storage_.size() <= max_frames_);
}
void EncodingEventSubscriber::OnReceivePacketEvent(
const PacketEvent& packet_event) {
DCHECK(thread_checker_.CalledOnValidThread());
if (event_media_type_ != packet_event.media_type)
return;
const RtpTimeDelta relative_rtp_timestamp =
GetRelativeRtpTimestamp(packet_event.rtp_timestamp);
uint32_t lower_32_bits = relative_rtp_timestamp.lower_32_bits();
auto it = packet_event_map_.find(relative_rtp_timestamp);
BasePacketEvent* base_packet_event_proto = nullptr;
// Look up existing entry. If not found, create a new entry and add to map.
if (it == packet_event_map_.end()) {
if (!ShouldCreateNewProto(lower_32_bits))
return;
IncrementStoredProtoCount(lower_32_bits);
auto event_proto = std::make_unique<AggregatedPacketEvent>();
event_proto->set_relative_rtp_timestamp(lower_32_bits);
base_packet_event_proto = GetNewBasePacketEvent(
event_proto.get(), packet_event.packet_id, packet_event.size);
packet_event_map_.insert(
std::make_pair(relative_rtp_timestamp, std::move(event_proto)));
} else {
// Found existing entry, now look up existing BasePacketEvent using packet
// ID. If not found, create a new entry and add to proto.
RepeatedPtrField<BasePacketEvent>* field =
it->second->mutable_base_packet_event();
for (RepeatedPtrField<BasePacketEvent>::pointer_iterator base_it =
field->pointer_begin();
base_it != field->pointer_end();
++base_it) {
if ((*base_it)->packet_id() == packet_event.packet_id) {
base_packet_event_proto = *base_it;
break;
}
}
if (!base_packet_event_proto) {
if (it->second->base_packet_event_size() >= kMaxPacketsPerFrame) {
DVLOG(3) << "Too many packets in AggregatedPacketEvent "
<< packet_event.rtp_timestamp << ". "
<< "Using new packet event proto.";
AddPacketEventToStorage(std::move(it->second));
if (!ShouldCreateNewProto(lower_32_bits)) {
packet_event_map_.erase(it);
return;
}
IncrementStoredProtoCount(lower_32_bits);
it->second = std::make_unique<AggregatedPacketEvent>();
it->second->set_relative_rtp_timestamp(lower_32_bits);
}
base_packet_event_proto = GetNewBasePacketEvent(
it->second.get(), packet_event.packet_id, packet_event.size);
} else if (base_packet_event_proto->event_type_size() >=
kMaxEventsPerProto) {
DVLOG(3) << "Too many events in packet "
<< packet_event.rtp_timestamp << ", "
<< packet_event.packet_id << ". Using new packet event proto.";
AddPacketEventToStorage(std::move(it->second));
if (!ShouldCreateNewProto(lower_32_bits)) {
packet_event_map_.erase(it);
return;
}
IncrementStoredProtoCount(lower_32_bits);
it->second = std::make_unique<AggregatedPacketEvent>();
it->second->set_relative_rtp_timestamp(lower_32_bits);
base_packet_event_proto = GetNewBasePacketEvent(
it->second.get(), packet_event.packet_id, packet_event.size);
}
}
base_packet_event_proto->add_event_type(
ToProtoEventType(packet_event.type));
base_packet_event_proto->add_event_timestamp_ms(
(packet_event.timestamp - base::TimeTicks()).InMilliseconds());
// |base_packet_event_proto| could have been created with a receiver event
// which does not have the packet size and we would need to overwrite it when
// we see a sender event, which does have the packet size.
if (packet_event.size > 0) {
base_packet_event_proto->set_size(packet_event.size);
}
if (packet_event_map_.size() > kMaxMapSize)
TransferPacketEvents(kNumMapEntriesToTransfer);
DCHECK(packet_event_map_.size() <= kMaxMapSize);
DCHECK(packet_event_storage_.size() <= max_frames_);
}
void EncodingEventSubscriber::GetEventsAndReset(LogMetadata* metadata,
FrameEventList* frame_events, PacketEventList* packet_events) {
DCHECK(thread_checker_.CalledOnValidThread());
// Flush all events.
TransferFrameEvents(frame_event_map_.size());
TransferPacketEvents(packet_event_map_.size());
std::sort(frame_event_storage_.begin(), frame_event_storage_.end(),
&IsRtpTimestampLessThan<std::unique_ptr<AggregatedFrameEvent>>);
std::sort(packet_event_storage_.begin(), packet_event_storage_.end(),
&IsRtpTimestampLessThan<std::unique_ptr<AggregatedPacketEvent>>);
metadata->set_is_audio(event_media_type_ == AUDIO_EVENT);
metadata->set_first_rtp_timestamp(first_rtp_timestamp_.lower_32_bits());
metadata->set_num_frame_events(frame_event_storage_.size());
metadata->set_num_packet_events(packet_event_storage_.size());
metadata->set_reference_timestamp_ms_at_unix_epoch(
(base::TimeTicks::UnixEpoch() - base::TimeTicks()).InMilliseconds());
frame_events->swap(frame_event_storage_);
packet_events->swap(packet_event_storage_);
Reset();
}
void EncodingEventSubscriber::TransferFrameEvents(size_t max_num_entries) {
DCHECK(frame_event_map_.size() >= max_num_entries);
auto it = frame_event_map_.begin();
for (size_t i = 0;
i < max_num_entries && it != frame_event_map_.end();
i++, ++it) {
AddFrameEventToStorage(std::move(it->second));
}
frame_event_map_.erase(frame_event_map_.begin(), it);
}
void EncodingEventSubscriber::TransferPacketEvents(size_t max_num_entries) {
auto it = packet_event_map_.begin();
for (size_t i = 0;
i < max_num_entries && it != packet_event_map_.end();
i++, ++it) {
AddPacketEventToStorage(std::move(it->second));
}
packet_event_map_.erase(packet_event_map_.begin(), it);
}
void EncodingEventSubscriber::AddFrameEventToStorage(
std::unique_ptr<AggregatedFrameEvent> frame_event_proto) {
if (frame_event_storage_.size() >= max_frames_) {
auto& entry = frame_event_storage_[frame_event_storage_index_];
DecrementStoredProtoCount(entry->relative_rtp_timestamp());
entry = std::move(frame_event_proto);
} else {
frame_event_storage_.push_back(std::move(frame_event_proto));
}
frame_event_storage_index_ = (frame_event_storage_index_ + 1) % max_frames_;
}
void EncodingEventSubscriber::AddPacketEventToStorage(
std::unique_ptr<AggregatedPacketEvent> packet_event_proto) {
if (packet_event_storage_.size() >= max_frames_) {
auto& entry = packet_event_storage_[packet_event_storage_index_];
DecrementStoredProtoCount(entry->relative_rtp_timestamp());
entry = std::move(packet_event_proto);
} else {
packet_event_storage_.push_back(std::move(packet_event_proto));
}
packet_event_storage_index_ = (packet_event_storage_index_ + 1) % max_frames_;
}
bool EncodingEventSubscriber::ShouldCreateNewProto(
uint32_t relative_rtp_timestamp_lower_32_bits) const {
auto it = stored_proto_counts_.find(relative_rtp_timestamp_lower_32_bits);
int proto_count = it == stored_proto_counts_.end() ? 0 : it->second;
DVLOG_IF(2, proto_count >= kMaxProtosPerFrame)
<< relative_rtp_timestamp_lower_32_bits
<< " already reached max number of protos.";
return proto_count < kMaxProtosPerFrame;
}
void EncodingEventSubscriber::IncrementStoredProtoCount(
uint32_t relative_rtp_timestamp_lower_32_bits) {
stored_proto_counts_[relative_rtp_timestamp_lower_32_bits]++;
DCHECK_LE(stored_proto_counts_[relative_rtp_timestamp_lower_32_bits],
kMaxProtosPerFrame)
<< relative_rtp_timestamp_lower_32_bits
<< " exceeded max number of event protos.";
}
void EncodingEventSubscriber::DecrementStoredProtoCount(
uint32_t relative_rtp_timestamp_lower_32_bits) {
auto it = stored_proto_counts_.find(relative_rtp_timestamp_lower_32_bits);
DCHECK(it != stored_proto_counts_.end())
<< "no event protos for " << relative_rtp_timestamp_lower_32_bits;
if (it->second > 1)
it->second--;
else
stored_proto_counts_.erase(it);
}
RtpTimeDelta EncodingEventSubscriber::GetRelativeRtpTimestamp(
RtpTimeTicks rtp_timestamp) {
if (!seen_first_rtp_timestamp_) {
seen_first_rtp_timestamp_ = true;
first_rtp_timestamp_ = rtp_timestamp;
}
return rtp_timestamp - first_rtp_timestamp_;
}
void EncodingEventSubscriber::Reset() {
frame_event_map_.clear();
frame_event_storage_.clear();
frame_event_storage_index_ = 0;
packet_event_map_.clear();
packet_event_storage_.clear();
packet_event_storage_index_ = 0;
stored_proto_counts_.clear();
seen_first_rtp_timestamp_ = false;
first_rtp_timestamp_ = RtpTimeTicks();
}
} // namespace cast
} // namespace media