summaryrefslogtreecommitdiffstats
path: root/chromium/media/cast/logging/encoding_event_subscriber.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/cast/logging/encoding_event_subscriber.cc')
-rw-r--r--chromium/media/cast/logging/encoding_event_subscriber.cc286
1 files changed, 286 insertions, 0 deletions
diff --git a/chromium/media/cast/logging/encoding_event_subscriber.cc b/chromium/media/cast/logging/encoding_event_subscriber.cc
new file mode 100644
index 00000000000..48cc911ba80
--- /dev/null
+++ b/chromium/media/cast/logging/encoding_event_subscriber.cc
@@ -0,0 +1,286 @@
+// 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 <cstring>
+#include <utility>
+
+#include "base/logging.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 media {
+namespace cast {
+
+EncodingEventSubscriber::EncodingEventSubscriber(
+ EventMediaType event_media_type,
+ size_t max_frames)
+ : event_media_type_(event_media_type),
+ max_frames_(max_frames),
+ frame_event_storage_index_(0),
+ packet_event_storage_index_(0),
+ seen_first_rtp_timestamp_(false),
+ first_rtp_timestamp_(0u) {}
+
+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;
+
+ RtpTimestamp relative_rtp_timestamp =
+ GetRelativeRtpTimestamp(frame_event.rtp_timestamp);
+ FrameEventMap::iterator it = frame_event_map_.find(relative_rtp_timestamp);
+ linked_ptr<AggregatedFrameEvent> event_proto;
+
+ // Look up existing entry. If not found, create a new entry and add to map.
+ if (it == frame_event_map_.end()) {
+ event_proto.reset(new AggregatedFrameEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ frame_event_map_.insert(
+ std::make_pair(relative_rtp_timestamp, event_proto));
+ } else {
+ event_proto = it->second;
+ if (event_proto->event_type_size() >= kMaxEventsPerProto) {
+ DVLOG(2) << "Too many events in frame " << frame_event.rtp_timestamp
+ << ". Using new frame event proto.";
+ AddFrameEventToStorage(event_proto);
+ event_proto.reset(new AggregatedFrameEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ }
+ }
+
+ event_proto->add_event_type(ToProtoEventType(frame_event.type));
+ event_proto->add_event_timestamp_ms(
+ (frame_event.timestamp - base::TimeTicks()).InMilliseconds());
+
+ if (frame_event.type == FRAME_ENCODED) {
+ event_proto->set_encoded_frame_size(frame_event.size);
+ if (frame_event.media_type == VIDEO_EVENT) {
+ event_proto->set_encoded_frame_size(frame_event.size);
+ event_proto->set_key_frame(frame_event.key_frame);
+ event_proto->set_target_bitrate(frame_event.target_bitrate);
+ }
+ } else if (frame_event.type == FRAME_PLAYOUT) {
+ event_proto->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;
+
+ RtpTimestamp relative_rtp_timestamp =
+ GetRelativeRtpTimestamp(packet_event.rtp_timestamp);
+ PacketEventMap::iterator it =
+ packet_event_map_.find(relative_rtp_timestamp);
+ linked_ptr<AggregatedPacketEvent> event_proto;
+ BasePacketEvent* base_packet_event_proto = NULL;
+
+ // Look up existing entry. If not found, create a new entry and add to map.
+ if (it == packet_event_map_.end()) {
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ packet_event_map_.insert(
+ std::make_pair(relative_rtp_timestamp, event_proto));
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.get(), packet_event.packet_id, packet_event.size);
+ } else {
+ // Found existing entry, now look up existing BasePacketEvent using packet
+ // ID. If not found, create a new entry and add to proto.
+ event_proto = it->second;
+ RepeatedPtrField<BasePacketEvent>* field =
+ event_proto->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 (event_proto->base_packet_event_size() >= kMaxPacketsPerFrame) {
+ DVLOG(3) << "Too many packets in AggregatedPacketEvent "
+ << packet_event.rtp_timestamp << ". "
+ << "Using new packet event proto.";
+ AddPacketEventToStorage(event_proto);
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ }
+
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.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(event_proto);
+ event_proto.reset(new AggregatedPacketEvent);
+ event_proto->set_relative_rtp_timestamp(relative_rtp_timestamp);
+ it->second = event_proto;
+ base_packet_event_proto = GetNewBasePacketEvent(
+ event_proto.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<linked_ptr<AggregatedFrameEvent> >);
+ std::sort(packet_event_storage_.begin(), packet_event_storage_.end(),
+ &IsRtpTimestampLessThan<linked_ptr<AggregatedPacketEvent> >);
+
+ metadata->set_is_audio(event_media_type_ == AUDIO_EVENT);
+ metadata->set_first_rtp_timestamp(first_rtp_timestamp_);
+ 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);
+
+ FrameEventMap::iterator it = frame_event_map_.begin();
+ for (size_t i = 0;
+ i < max_num_entries && it != frame_event_map_.end();
+ i++, ++it) {
+ AddFrameEventToStorage(it->second);
+ }
+
+ frame_event_map_.erase(frame_event_map_.begin(), it);
+}
+
+void EncodingEventSubscriber::TransferPacketEvents(size_t max_num_entries) {
+ PacketEventMap::iterator it = packet_event_map_.begin();
+ for (size_t i = 0;
+ i < max_num_entries && it != packet_event_map_.end();
+ i++, ++it) {
+ AddPacketEventToStorage(it->second);
+ }
+
+ packet_event_map_.erase(packet_event_map_.begin(), it);
+}
+
+void EncodingEventSubscriber::AddFrameEventToStorage(
+ const linked_ptr<AggregatedFrameEvent>& frame_event_proto) {
+ if (frame_event_storage_.size() >= max_frames_) {
+ frame_event_storage_[frame_event_storage_index_] = frame_event_proto;
+ } else {
+ frame_event_storage_.push_back(frame_event_proto);
+ }
+
+ frame_event_storage_index_ = (frame_event_storage_index_ + 1) % max_frames_;
+}
+
+void EncodingEventSubscriber::AddPacketEventToStorage(
+ const linked_ptr<AggregatedPacketEvent>& packet_event_proto) {
+ if (packet_event_storage_.size() >= max_frames_)
+ packet_event_storage_[packet_event_storage_index_] = packet_event_proto;
+ else
+ packet_event_storage_.push_back(packet_event_proto);
+
+ packet_event_storage_index_ = (packet_event_storage_index_ + 1) % max_frames_;
+}
+
+RtpTimestamp EncodingEventSubscriber::GetRelativeRtpTimestamp(
+ RtpTimestamp 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;
+ seen_first_rtp_timestamp_ = false;
+ first_rtp_timestamp_ = 0u;
+}
+
+} // namespace cast
+} // namespace media