summaryrefslogtreecommitdiff
path: root/media/cast
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-11-06 12:27:47 +0000
committerTorne (Richard Coles) <torne@google.com>2013-11-06 12:27:47 +0000
commit0f1bc08d4cfcc34181b0b5cbf065c40f687bf740 (patch)
tree08e3fb2fdca3674ceb4d6cf527cb65e755cd993e /media/cast
parent0bdaf95291fc46702f274f40e8e5081e9ef23011 (diff)
downloadchromium_org-0f1bc08d4cfcc34181b0b5cbf065c40f687bf740.tar.gz
Merge from Chromium at DEPS revision 232870
This commit was generated by merge_to_master.py. Change-Id: I9cd2139013538e8bcd17966e8ff30ca5651d1a3d
Diffstat (limited to 'media/cast')
-rw-r--r--media/cast/audio_receiver/audio_receiver.cc26
-rw-r--r--media/cast/audio_receiver/audio_receiver.h6
-rw-r--r--media/cast/audio_receiver/audio_receiver_unittest.cc5
-rw-r--r--media/cast/cast_defines.h41
-rw-r--r--media/cast/logging/logging.gyp11
-rw-r--r--media/cast/logging/logging_defines.cc79
-rw-r--r--media/cast/logging/logging_defines.h111
-rw-r--r--media/cast/logging/logging_impl.cc196
-rw-r--r--media/cast/logging/logging_impl.h72
-rw-r--r--media/cast/logging/logging_raw.cc139
-rw-r--r--media/cast/logging/logging_raw.h85
-rw-r--r--media/cast/logging/logging_stats.cc149
-rw-r--r--media/cast/logging/logging_stats.h77
-rw-r--r--media/cast/logging/logging_unittest.cc248
-rw-r--r--media/cast/rtcp/rtcp.cc8
-rw-r--r--media/cast/rtcp/rtcp_unittest.cc70
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc3
-rw-r--r--media/cast/rtp_sender/rtp_sender.cc2
-rw-r--r--media/cast/test/encode_decode_test.cc1
-rw-r--r--media/cast/test/end2end_unittest.cc9
20 files changed, 1228 insertions, 110 deletions
diff --git a/media/cast/audio_receiver/audio_receiver.cc b/media/cast/audio_receiver/audio_receiver.cc
index 1a1c8e7607..c3dc3937b9 100644
--- a/media/cast/audio_receiver/audio_receiver.cc
+++ b/media/cast/audio_receiver/audio_receiver.cc
@@ -119,6 +119,7 @@ AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment,
audio_config.rtcp_c_name));
rtcp_->SetRemoteSSRC(audio_config.incoming_ssrc);
ScheduleNextRtcpReport();
+ ScheduleNextCastMessage();
}
AudioReceiver::~AudioReceiver() {}
@@ -190,6 +191,7 @@ void AudioReceiver::DecodeAudioFrameThread(
}
void AudioReceiver::PlayoutTimeout() {
+ DCHECK(audio_buffer_) << "Invalid function call in this configuration";
if (queued_encoded_callbacks_.empty()) {
// Already released by incoming packet.
return;
@@ -242,6 +244,7 @@ bool AudioReceiver::PostEncodedAudioFrame(
uint32 rtp_timestamp,
bool next_frame,
scoped_ptr<EncodedAudioFrame>* encoded_frame) {
+ DCHECK(audio_buffer_) << "Invalid function call in this configuration";
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
base::TimeTicks playout_time = GetPlayoutTime(now, rtp_timestamp);
base::TimeDelta time_until_playout = playout_time - now;
@@ -328,5 +331,28 @@ void AudioReceiver::SendNextRtcpReport() {
ScheduleNextRtcpReport();
}
+// Cast messages should be sent within a maximum interval. Schedule a call
+// if not triggered elsewhere, e.g. by the cast message_builder.
+void AudioReceiver::ScheduleNextCastMessage() {
+ if (audio_buffer_) {
+ base::TimeTicks send_time;
+ audio_buffer_->TimeToSendNextCastMessage(&send_time);
+
+ base::TimeDelta time_to_send = send_time -
+ cast_environment_->Clock()->NowTicks();
+ time_to_send = std::max(time_to_send,
+ base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+ cast_environment_->PostDelayedTask(CastEnvironment::MAIN, FROM_HERE,
+ base::Bind(&AudioReceiver::SendNextCastMessage,
+ weak_factory_.GetWeakPtr()), time_to_send);
+ }
+}
+
+void AudioReceiver::SendNextCastMessage() {
+ DCHECK(audio_buffer_) << "Invalid function call in this configuration";
+ audio_buffer_->SendCastMessage(); // Will only send a message if it is time.
+ ScheduleNextCastMessage();
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/audio_receiver/audio_receiver.h b/media/cast/audio_receiver/audio_receiver.h
index 742d3433d8..2044e16cfc 100644
--- a/media/cast/audio_receiver/audio_receiver.h
+++ b/media/cast/audio_receiver/audio_receiver.h
@@ -87,6 +87,12 @@ class AudioReceiver : public base::NonThreadSafe,
// Actually send the next RTCP report.
void SendNextRtcpReport();
+ // Schedule timing for the next cast message.
+ void ScheduleNextCastMessage();
+
+ // Actually send the next cast message.
+ void SendNextCastMessage();
+
scoped_refptr<CastEnvironment> cast_environment_;
base::WeakPtrFactory<AudioReceiver> weak_factory_;
diff --git a/media/cast/audio_receiver/audio_receiver_unittest.cc b/media/cast/audio_receiver/audio_receiver_unittest.cc
index bb26cb9dac..cbd90c1215 100644
--- a/media/cast/audio_receiver/audio_receiver_unittest.cc
+++ b/media/cast/audio_receiver/audio_receiver_unittest.cc
@@ -132,7 +132,8 @@ TEST_F(AudioReceiverTest, GetOnePacketEncodedframe) {
TEST_F(AudioReceiverTest, MultiplePendingGetCalls) {
Configure(true);
- EXPECT_CALL(mock_transport_, SendRtcpPacket(testing::_)).Times(2);
+ EXPECT_CALL(mock_transport_, SendRtcpPacket(testing::_)).WillRepeatedly(
+ testing::Return(true));
AudioFrameEncodedCallback frame_encoded_callback =
base::Bind(&TestAudioEncoderCallback::DeliverEncodedAudioFrame,
@@ -154,7 +155,7 @@ TEST_F(AudioReceiverTest, MultiplePendingGetCalls) {
uint32 ntp_high;
uint32 ntp_low;
- ConvertTimeToNtp(testing_clock_.NowTicks(), &ntp_high, &ntp_low);
+ ConvertTimeTicksToNtp(testing_clock_.NowTicks(), &ntp_high, &ntp_low);
rtcp_packet.AddSrWithNtp(audio_config_.feedback_ssrc, ntp_high, ntp_low,
rtp_header_.webrtc.header.timestamp);
diff --git a/media/cast/cast_defines.h b/media/cast/cast_defines.h
index 0778638551..f44e5fb826 100644
--- a/media/cast/cast_defines.h
+++ b/media/cast/cast_defines.h
@@ -54,16 +54,15 @@ typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
// TODO(pwestin): Re-factor the functions bellow into a class with static
// methods.
+// January 1970, in NTP seconds.
+// Network Time Protocol (NTP), which is in seconds relative to 0h UTC on
+// 1 January 1900.
+static const int64 kUnixEpochInNtpSeconds = GG_INT64_C(2208988800);
+
// Magic fractional unit. Used to convert time (in microseconds) to/from
// fractional NTP seconds.
static const double kMagicFractionalUnit = 4.294967296E3;
-// Network Time Protocol (NTP), which is in seconds relative to 0h UTC on
-// 1 January 1900.
-static const int64 kNtpEpochDeltaSeconds = GG_INT64_C(9435484800);
-static const int64 kNtpEpochDeltaMicroseconds =
- kNtpEpochDeltaSeconds * base::Time::kMicrosecondsPerSecond;
-
inline bool IsNewerFrameId(uint8 frame_id, uint8 prev_frame_id) {
return (frame_id != prev_frame_id) &&
static_cast<uint8>(frame_id - prev_frame_id) < 0x80;
@@ -107,20 +106,28 @@ inline void ConvertTimeToFractions(int64 time_us,
(time_us % base::Time::kMicrosecondsPerSecond) * kMagicFractionalUnit);
}
-inline void ConvertTimeToNtp(const base::TimeTicks& time,
- uint32* ntp_seconds,
- uint32* ntp_fractions) {
- int64 time_us = time.ToInternalValue() - kNtpEpochDeltaMicroseconds;
- ConvertTimeToFractions(time_us, ntp_seconds, ntp_fractions);
+inline void ConvertTimeTicksToNtp(const base::TimeTicks& time,
+ uint32* ntp_seconds,
+ uint32* ntp_fractions) {
+ base::TimeDelta elapsed_since_unix_epoch =
+ time - base::TimeTicks::UnixEpoch();
+
+ int64 ntp_time_us = elapsed_since_unix_epoch.InMicroseconds() +
+ (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond);
+
+ ConvertTimeToFractions(ntp_time_us, ntp_seconds, ntp_fractions);
}
-inline base::TimeTicks ConvertNtpToTime(uint32 ntp_seconds,
- uint32 ntp_fractions) {
+inline base::TimeTicks ConvertNtpToTimeTicks(uint32 ntp_seconds,
+ uint32 ntp_fractions) {
int64 ntp_time_us = static_cast<int64>(ntp_seconds) *
- base::Time::kMicrosecondsPerSecond;
- ntp_time_us += static_cast<int64>(ntp_fractions) / kMagicFractionalUnit;
- return base::TimeTicks::FromInternalValue(ntp_time_us +
- kNtpEpochDeltaMicroseconds);
+ base::Time::kMicrosecondsPerSecond +
+ static_cast<int64>(ntp_fractions) / kMagicFractionalUnit;
+
+ base::TimeDelta elapsed_since_unix_epoch =
+ base::TimeDelta::FromMicroseconds(ntp_time_us -
+ (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond));
+ return base::TimeTicks::UnixEpoch() + elapsed_since_unix_epoch;
}
} // namespace cast
diff --git a/media/cast/logging/logging.gyp b/media/cast/logging/logging.gyp
index d1953482e8..9140e3a6eb 100644
--- a/media/cast/logging/logging.gyp
+++ b/media/cast/logging/logging.gyp
@@ -12,11 +12,14 @@
'<(DEPTH)/',
],
'sources': [
- 'logging.cc',
- 'logging.h',
+ 'logging_defines.cc',
'logging_defines.h',
- 'logging_internal.cc',
- 'logging_internal.h',
+ 'logging_impl.cc',
+ 'logging_impl.h',
+ 'logging_raw.cc',
+ 'logging_raw.h',
+ 'logging_stats.cc',
+ 'logging_stats.h',
],
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
diff --git a/media/cast/logging/logging_defines.cc b/media/cast/logging/logging_defines.cc
new file mode 100644
index 0000000000..3f502f9eaa
--- /dev/null
+++ b/media/cast/logging/logging_defines.cc
@@ -0,0 +1,79 @@
+// Copyright 2013 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/logging_defines.h"
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+
+std::string CastLoggingToString(CastLoggingEvent event) {
+ switch (event) {
+ case(kRtt):
+ return "Rtt";
+ case(kPacketLoss):
+ return "PacketLoss";
+ case(kJitter):
+ return "Jitter";
+ case(kAckReceived):
+ return "AckReceived";
+ case(kAckSent):
+ return "AckSent";
+ case(kLastEvent):
+ return "LastEvent";
+ case(kAudioFrameCaptured):
+ return "AudioFrameCaptured";
+ case(kAudioFrameEncoded):
+ return "AudioFrameEncoded";
+ case(kAudioPlayoutDelay):
+ return "AudioPlayoutDelay";
+ case(kAudioFrameDecoded):
+ return "AudioFrameDecoded";
+ case(kVideoFrameCaptured):
+ return "VideoFrameCaptured";
+ case(kVideoFrameSentToEncoder):
+ return "VideoFrameSentToEncoder";
+ case(kVideoFrameEncoded):
+ return "VideoFrameEncoded";
+ case(kVideoFrameDecoded):
+ return "VideoFrameDecoded";
+ case(kVideoRenderDelay):
+ return "VideoRenderDelay";
+ case(kPacketSentToPacer):
+ return "PacketSentToPacer";
+ case(kPacketSentToNetwork):
+ return "PacketSentToNetwork";
+ case(kPacketRetransmited):
+ return "PacketRetransmited";
+ case(kPacketReceived):
+ return "PacketReceived";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+FrameEvent::FrameEvent() {}
+FrameEvent::~FrameEvent() {}
+
+BasePacketInfo::BasePacketInfo() {}
+BasePacketInfo::~BasePacketInfo() {}
+
+PacketEvent::PacketEvent() {}
+PacketEvent::~PacketEvent() {}
+
+GenericEvent::GenericEvent() {}
+GenericEvent::~GenericEvent() {}
+
+FrameLogStats::FrameLogStats()
+ : framerate_fps(0),
+ bitrate_kbps(0),
+ max_delay_ms(0),
+ min_delay_ms(0),
+ avg_delay_ms(0) {}
+FrameLogStats::~FrameLogStats() {}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/logging/logging_defines.h b/media/cast/logging/logging_defines.h
index 724216dd30..835c022589 100644
--- a/media/cast/logging/logging_defines.h
+++ b/media/cast/logging/logging_defines.h
@@ -5,7 +5,12 @@
#ifndef MEDIA_CAST_LOGGING_LOGGING_DEFINES_H_
#define MEDIA_CAST_LOGGING_LOGGING_DEFINES_H_
-#include "base/logging.h"
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/linked_ptr.h"
+#include "base/time/time.h"
namespace media {
namespace cast {
@@ -39,51 +44,65 @@ enum CastLoggingEvent {
kPacketReceived,
};
-std::string CastEnumToString(CastLoggingEvent event) {
- switch (event) {
- case(kRtt):
- return "Rtt";
- case(kPacketLoss):
- return "PacketLoss";
- case(kJitter):
- return "Jitter";
- case(kAckReceived):
- return "AckReceived";
- case(kAckSent):
- return "AckSent";
- case(kLastEvent):
- return "LastEvent";
- case(kAudioFrameCaptured):
- return "AudioFrameCaptured";
- case(kAudioFrameEncoded):
- return "AudioFrameEncoded";
- case(kAudioPlayoutDelay):
- return "AudioPlayoutDelay";
- case(kAudioFrameDecoded):
- return "AudioFrameDecoded";
- case(kVideoFrameCaptured):
- return "VideoFrameCaptured";
- case(kVideoFrameSentToEncoder):
- return "VideoFrameSentToEncoder";
- case(kVideoFrameEncoded):
- return "VideoFrameEncoded";
- case(kVideoFrameDecoded):
- return "VideoFrameDecoded";
- case(kVideoRenderDelay):
- return "VideoRenderDelay";
- case(kPacketSentToPacer):
- return "PacketSentToPacer";
- case(kPacketSentToNetwork):
- return "PacketSentToNetwork";
- case(kPacketRetransmited):
- return "PacketRetransmited";
- case(kPacketReceived):
- return "PacketReceived";
- default:
- NOTREACHED();
- return "";
- }
-}
+std::string CastLoggingToString(CastLoggingEvent event);
+
+struct FrameEvent {
+ FrameEvent();
+ ~FrameEvent();
+
+ uint8 frame_id;
+ int size; // Encoded size only.
+ std::vector<base::TimeTicks> timestamp;
+ std::vector<CastLoggingEvent> type;
+ base::TimeDelta delay_delta; // Render/playout delay.
+};
+
+// Internal map sorted by packet id.
+struct BasePacketInfo {
+ BasePacketInfo();
+ ~BasePacketInfo();
+
+ int size;
+ std::vector<base::TimeTicks> timestamp;
+ std::vector<CastLoggingEvent> type;
+};
+
+typedef std::map<uint16, BasePacketInfo> BasePacketMap;
+
+struct PacketEvent {
+ PacketEvent();
+ ~PacketEvent();
+ uint8 frame_id;
+ int max_packet_id;
+ BasePacketMap packet_map;
+};
+
+struct GenericEvent {
+ GenericEvent();
+ ~GenericEvent();
+ std::vector<int> value;
+ std::vector<base::TimeTicks> timestamp;
+};
+
+struct FrameLogStats {
+ FrameLogStats();
+ ~FrameLogStats();
+
+ double framerate_fps;
+ double bitrate_kbps;
+ int max_delay_ms;
+ int min_delay_ms;
+ int avg_delay_ms;
+};
+
+// Store all log types in a map based on the event.
+typedef std::map<uint32, FrameEvent> FrameRawMap;
+typedef std::map<uint32, PacketEvent> PacketRawMap;
+typedef std::map<CastLoggingEvent, GenericEvent> GenericRawMap;
+
+typedef std::map<CastLoggingEvent, linked_ptr<FrameLogStats > > FrameStatsMap;
+typedef std::map<CastLoggingEvent, double> PacketStatsMap;
+typedef std::map<CastLoggingEvent, double> GenericStatsMap;
} // namespace cast
} // namespace media
diff --git a/media/cast/logging/logging_impl.cc b/media/cast/logging/logging_impl.cc
new file mode 100644
index 0000000000..7f91df2737
--- /dev/null
+++ b/media/cast/logging/logging_impl.cc
@@ -0,0 +1,196 @@
+// Copyright 2013 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 "base/debug/trace_event.h"
+#include "base/metrics/histogram.h"
+#include "media/cast/logging/logging_impl.h"
+
+namespace media {
+namespace cast {
+
+LoggingImpl::LoggingImpl(base::TickClock* clock,
+ bool enable_data_collection,
+ bool enable_uma_stats,
+ bool enable_tracing)
+ : enable_data_collection_(enable_data_collection),
+ enable_uma_stats_(enable_uma_stats),
+ enable_tracing_(enable_tracing),
+ raw_(clock),
+ stats_(clock) {}
+
+LoggingImpl::~LoggingImpl() {}
+
+void LoggingImpl::InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id) {
+ if (enable_data_collection_) {
+ raw_.InsertFrameEvent(event, rtp_timestamp, frame_id);
+ stats_.InsertFrameEvent(event, rtp_timestamp, frame_id);
+ }
+ if (enable_tracing_) {
+ std::string event_string = CastLoggingToString(event);
+ TRACE_EVENT_INSTANT2(event_string.c_str(), "FE",
+ TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "frame_id",
+ frame_id);
+ }
+}
+
+void LoggingImpl::InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int frame_size) {
+ if (enable_data_collection_) {
+ raw_.InsertFrameEventWithSize(event, rtp_timestamp, frame_id, frame_size);
+ stats_.InsertFrameEventWithSize(event, rtp_timestamp, frame_id, frame_size);
+ }
+ if (enable_uma_stats_) {
+ UMA_HISTOGRAM_COUNTS(CastLoggingToString(event), frame_size);
+ }
+ if (enable_tracing_) {
+ std::string event_string = CastLoggingToString(event);
+ TRACE_EVENT_INSTANT2(event_string.c_str(), "FES",
+ TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "frame_size",
+ frame_size);
+ }
+
+}
+
+void LoggingImpl::InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay) {
+ if (enable_data_collection_) {
+ raw_.InsertFrameEventWithDelay(event, rtp_timestamp, frame_id, delay);
+ stats_.InsertFrameEventWithDelay(event, rtp_timestamp, frame_id, delay);
+ }
+ if (enable_uma_stats_) {
+ UMA_HISTOGRAM_TIMES(CastLoggingToString(event), delay);
+ }
+ if (enable_tracing_) {
+ std::string event_string = CastLoggingToString(event);
+ TRACE_EVENT_INSTANT2(event_string.c_str(), "FED",
+ TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp, "delay",
+ delay.InMilliseconds());
+ }
+}
+
+void LoggingImpl::InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size) {
+ if (enable_data_collection_) {
+ raw_.InsertPacketEvent(event, rtp_timestamp, frame_id, packet_id,
+ max_packet_id, size);
+ stats_.InsertPacketEvent(event, rtp_timestamp, frame_id, packet_id,
+ max_packet_id, size);
+ }
+ if (enable_tracing_) {
+ std::string event_string = CastLoggingToString(event);
+ TRACE_EVENT_INSTANT2(event_string.c_str(), "PE",
+ TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp,
+ "packet_id", packet_id);
+ }
+}
+
+void LoggingImpl::InsertGenericEvent(CastLoggingEvent event, int value) {
+ if (enable_data_collection_) {
+ raw_.InsertGenericEvent(event, value);
+ stats_.InsertGenericEvent(event, value);
+ }
+ if (enable_uma_stats_) {
+ UMA_HISTOGRAM_COUNTS(CastLoggingToString(event), value);
+ }
+ if (enable_tracing_) {
+ std::string event_string = CastLoggingToString(event);
+ TRACE_EVENT_INSTANT1(event_string.c_str(), "GE",
+ TRACE_EVENT_SCOPE_THREAD, "value", value);
+ }
+}
+
+
+// should just get the entire class, would be much easier.
+FrameRawMap LoggingImpl::GetFrameRawData() {
+ return raw_.GetFrameData();
+}
+
+PacketRawMap LoggingImpl::GetPacketRawData() {
+ return raw_.GetPacketData();
+}
+
+GenericRawMap LoggingImpl::GetGenericRawData() {
+ return raw_.GetGenericData();
+}
+
+const FrameStatsMap* LoggingImpl::GetFrameStatsData() {
+ // Get stats data.
+ const FrameStatsMap* stats = stats_.GetFrameStatsData();
+ if (enable_uma_stats_) {
+ FrameStatsMap::const_iterator it;
+ for (it = stats->begin(); it != stats->end(); ++it) {
+ // Check for an active event.
+ if (it->second->framerate_fps > 0) {
+ std::string event_string = CastLoggingToString(it->first);
+ UMA_HISTOGRAM_COUNTS(event_string.append("_framerate_fps"),
+ it->second->framerate_fps);
+ } else {
+ // All active frame events trigger framerate computation.
+ continue;
+ }
+ if (it->second->bitrate_kbps > 0) {
+ std::string evnt_string = CastLoggingToString(it->first);
+ UMA_HISTOGRAM_COUNTS(evnt_string.append("_bitrate_kbps"),
+ it->second->framerate_fps);
+ }
+ if (it->second->avg_delay_ms > 0) {
+ std::string event_string = CastLoggingToString(it->first);
+ UMA_HISTOGRAM_COUNTS(event_string.append("_avg_delay_ms"),
+ it->second->avg_delay_ms);
+ UMA_HISTOGRAM_COUNTS(event_string.append("_min_delay_ms"),
+ it->second->min_delay_ms);
+ UMA_HISTOGRAM_COUNTS(event_string.append("_max_delay_ms"),
+ it->second->max_delay_ms);
+ }
+ }
+ }
+ return stats;
+}
+
+const PacketStatsMap* LoggingImpl::GetPacketStatsData() {
+ // Get stats data.
+ const PacketStatsMap* stats = stats_.GetPacketStatsData();
+ if (enable_uma_stats_) {
+ PacketStatsMap::const_iterator it;
+ for (it = stats->begin(); it != stats->end(); ++it) {
+ if (it->second > 0) {
+ std::string event_string = CastLoggingToString(it->first);
+ UMA_HISTOGRAM_COUNTS(event_string.append("_bitrate_kbps"), it->second);
+ }
+ }
+ }
+ return stats;
+}
+
+const GenericStatsMap* LoggingImpl::GetGenericStatsData() {
+ // Get stats data.
+ const GenericStatsMap* stats = stats_.GetGenericStatsData();
+ if (enable_uma_stats_) {
+ GenericStatsMap::const_iterator it;
+ for (it = stats->begin(); it != stats->end(); ++it) {
+ if (it->second > 0) {
+ UMA_HISTOGRAM_COUNTS(CastLoggingToString(it->first), it->second);
+ }
+ }
+ }
+ return stats;
+}
+
+void LoggingImpl::Reset() {
+ raw_.Reset();
+ stats_.Reset();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/logging/logging_impl.h b/media/cast/logging/logging_impl.h
new file mode 100644
index 0000000000..568bff5360
--- /dev/null
+++ b/media/cast/logging/logging_impl.h
@@ -0,0 +1,72 @@
+// Copyright 2013 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.
+#ifndef MEDIA_CAST_LOGGING_LOGGING_IMPL_H_
+#define MEDIA_CAST_LOGGING_LOGGING_IMPL_H_
+
+// Generic class that handles event logging for the cast library.
+// Logging has three possible optional forms:
+// 1. Raw data and stats accessible by the application.
+// 2. UMA stats.
+// 3. Tracing of raw events.
+
+#include "media/cast/logging/logging_defines.h"
+#include "media/cast/logging/logging_raw.h"
+#include "media/cast/logging/logging_stats.h"
+
+namespace media {
+namespace cast {
+
+class LoggingImpl {
+ public:
+ LoggingImpl(base::TickClock* clock,
+ bool enable_data_collection,
+ bool enable_uma_stats,
+ bool enable_tracing);
+
+ ~LoggingImpl();
+
+ void InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id);
+ void InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int frame_size);
+ void InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay);
+ void InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size);
+ void InsertGenericEvent(CastLoggingEvent event, int value);
+
+ // Get raw data.
+ FrameRawMap GetFrameRawData();
+ PacketRawMap GetPacketRawData();
+ GenericRawMap GetGenericRawData();
+ // Get stats only (computed when called). Triggers UMA stats when enabled.
+ const FrameStatsMap* GetFrameStatsData();
+ const PacketStatsMap* GetPacketStatsData();
+ const GenericStatsMap* GetGenericStatsData();
+
+ void Reset();
+
+ private:
+ LoggingRaw raw_;
+ LoggingStats stats_;
+ bool enable_data_collection_;
+ bool enable_uma_stats_;
+ bool enable_tracing_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoggingImpl);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_LOGGING_IMPL_H_
diff --git a/media/cast/logging/logging_raw.cc b/media/cast/logging/logging_raw.cc
new file mode 100644
index 0000000000..fa865a4324
--- /dev/null
+++ b/media/cast/logging/logging_raw.cc
@@ -0,0 +1,139 @@
+// Copyright 2013 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/logging_raw.h"
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+LoggingRaw::LoggingRaw(base::TickClock* clock)
+ : clock_(clock),
+ frame_map_(),
+ packet_map_(),
+ generic_map_(),
+ weak_factory_(this) {}
+
+LoggingRaw::~LoggingRaw() {}
+
+void LoggingRaw::InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+}
+
+void LoggingRaw::InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int size) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+ // Now insert size.
+ FrameRawMap::iterator it = frame_map_.find(rtp_timestamp);
+ DCHECK(it != frame_map_.end());
+ it->second.size = size;
+}
+
+void LoggingRaw::InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+ // Now insert delay.
+ FrameRawMap::iterator it = frame_map_.find(rtp_timestamp);
+ DCHECK(it != frame_map_.end());
+ it->second.delay_delta = delay;
+}
+void LoggingRaw::InsertBaseFrameEvent(CastLoggingEvent event,
+ uint8 frame_id,
+ uint32 rtp_timestamp) {
+ // Is this a new event?
+ FrameRawMap::iterator it = frame_map_.find(event);
+ if (it == frame_map_.end()) {
+ // Create a new map entry.
+ FrameEvent info;
+ info.frame_id = frame_id;
+ info.timestamp.push_back(clock_->NowTicks());
+ info.type.push_back(event);
+ frame_map_.insert(std::make_pair(rtp_timestamp, info));
+ } else {
+ // Insert to an existing entry.
+ it->second.timestamp.push_back(clock_->NowTicks());
+ it->second.type.push_back(event);
+ }
+}
+
+void LoggingRaw::InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size) {
+ // Is this packet belonging to a new frame?
+ PacketRawMap::iterator it = packet_map_.find(rtp_timestamp);
+ if (it == packet_map_.end()) {
+ // Create a new entry - start with base packet map.
+ PacketEvent info;
+ info.frame_id = frame_id;
+ info.max_packet_id = max_packet_id;
+ BasePacketInfo base_info;
+ base_info.size = size;
+ base_info.timestamp.push_back(clock_->NowTicks());
+ base_info.type.push_back(event);
+ packet_map_.insert(std::make_pair(rtp_timestamp, info));
+ } else {
+ // Is this a new packet?
+ BasePacketMap::iterator packet_it = it->second.packet_map.find(packet_id);
+ if (packet_it == it->second.packet_map.end()) {
+ BasePacketInfo base_info;
+ base_info.size = size;
+ base_info.timestamp.push_back(clock_->NowTicks());
+ base_info.type.push_back(event);
+ it->second.packet_map.insert(std::make_pair(packet_id, base_info));
+ } else {
+ packet_it->second.timestamp.push_back(clock_->NowTicks());
+ packet_it->second.type.push_back(event);
+ }
+ }
+}
+
+void LoggingRaw::InsertGenericEvent(CastLoggingEvent event, int value) {
+ GenericEvent event_data;
+ event_data.value.push_back(value);
+ event_data.timestamp.push_back(clock_->NowTicks());
+ // Is this a new event?
+ GenericRawMap::iterator it = generic_map_.find(event);
+ if (it == generic_map_.end()) {
+ // Create new entry.
+ generic_map_.insert(std::make_pair(event, event_data));
+ } else {
+ // Insert to existing entry.
+ it->second.value.push_back(value);
+ it->second.timestamp.push_back(clock_->NowTicks());
+ }
+}
+
+FrameRawMap LoggingRaw::GetFrameData() const {
+ return frame_map_;
+}
+
+PacketRawMap LoggingRaw::GetPacketData() const {
+ return packet_map_;
+}
+
+GenericRawMap LoggingRaw::GetGenericData() const {
+ return generic_map_;
+}
+
+void LoggingRaw::Reset() {
+ frame_map_.clear();
+ packet_map_.clear();
+ generic_map_.clear();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/logging/logging_raw.h b/media/cast/logging/logging_raw.h
new file mode 100644
index 0000000000..91de12aaaa
--- /dev/null
+++ b/media/cast/logging/logging_raw.h
@@ -0,0 +1,85 @@
+// Copyright 2013 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.
+
+#ifndef MEDIA_CAST_LOGGING_LOGGING_RAW_H_
+#define MEDIA_CAST_LOGGING_LOGGING_RAW_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/tick_clock.h"
+#include "media/cast/logging/logging_defines.h"
+
+namespace media {
+namespace cast {
+
+// This class is not thread safe, and should only be called from the main
+// thread.
+class LoggingRaw : public base::NonThreadSafe,
+ public base::SupportsWeakPtr<LoggingRaw> {
+ public:
+ explicit LoggingRaw(base::TickClock* clock);
+ ~LoggingRaw();
+
+ // Inform of new event: three types of events: frame, packets and generic.
+ // Frame events can be inserted with different parameters.
+ void InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id);
+
+ // Size - Inserting the size implies that this is an encoded frame.
+ void InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int frame_size);
+
+ // Render/playout delay
+ void InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay);
+
+ // Insert a packet event.
+ void InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size);
+
+ void InsertGenericEvent(CastLoggingEvent event, int value);
+
+ // Get raw log data.
+ FrameRawMap GetFrameData() const;
+ PacketRawMap GetPacketData() const;
+ GenericRawMap GetGenericData() const;
+
+
+ // Reset all log data.
+ void Reset();
+
+ private:
+ void InsertBaseFrameEvent(CastLoggingEvent event,
+ uint8 frame_id,
+ uint32 rtp_timestamp);
+
+ base::WeakPtrFactory<LoggingRaw> weak_factory_;
+ base::TickClock* const clock_; // Not owned by this class.
+ FrameRawMap frame_map_;
+ PacketRawMap packet_map_;
+ GenericRawMap generic_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoggingRaw);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_LOGGING_RAW_H_
+
diff --git a/media/cast/logging/logging_stats.cc b/media/cast/logging/logging_stats.cc
new file mode 100644
index 0000000000..9d79f69b6f
--- /dev/null
+++ b/media/cast/logging/logging_stats.cc
@@ -0,0 +1,149 @@
+// Copyright 2013 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 "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/logging/logging_stats.h"
+
+namespace media {
+namespace cast {
+
+LoggingStats::LoggingStats(base::TickClock* clock)
+ : frame_stats_(),
+ packet_stats_(),
+ generic_stats_(),
+ start_time_(),
+ clock_(clock) {
+ memset(counts_, 0, sizeof(counts_));
+}
+
+LoggingStats::~LoggingStats() {}
+
+void LoggingStats::Reset() {
+ frame_stats_.clear();
+ packet_stats_.clear();
+ generic_stats_.clear();
+ memset(counts_, 0, sizeof(counts_));
+}
+
+void LoggingStats::InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+}
+
+void LoggingStats::InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int frame_size) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+ // Update size.
+ FrameStatsMap::iterator it = frame_stats_.find(event);
+ DCHECK(it != frame_stats_.end());
+ it->second->bitrate_kbps += frame_size;
+}
+
+void LoggingStats::InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay) {
+ InsertBaseFrameEvent(event, frame_id, rtp_timestamp);
+ // Update size.
+ FrameStatsMap::iterator it = frame_stats_.find(event);
+ DCHECK(it != frame_stats_.end());
+ // Using the average delay as a counter, will divide by the counter when
+ // triggered.
+ it->second->avg_delay_ms += delay.InMilliseconds();
+ if (delay.InMilliseconds() > it->second->max_delay_ms)
+ it->second->max_delay_ms = delay.InMilliseconds();
+ if ((delay.InMilliseconds() < it->second->min_delay_ms) ||
+ (counts_[event] == 1) )
+ it->second->min_delay_ms = delay.InMilliseconds();
+}
+
+void LoggingStats::InsertBaseFrameEvent(CastLoggingEvent event,
+ uint8 frame_id,
+ uint32 rtp_timestamp) {
+ // Does this belong to an existing event?
+ FrameStatsMap::iterator it = frame_stats_.find(event);
+ if (it == frame_stats_.end()) {
+ // New event.
+ start_time_[event] = clock_->NowTicks();
+ linked_ptr<FrameLogStats> stats(new FrameLogStats());
+ frame_stats_.insert(std::make_pair(event, stats));
+ }
+
+ ++counts_[event];
+}
+
+void LoggingStats::InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size) {
+ // Does this packet belong to an existing event?
+ PacketStatsMap::iterator it = packet_stats_.find(event);
+ if (it == packet_stats_.end()) {
+ // New event.
+ start_time_[event] = clock_->NowTicks();
+ packet_stats_.insert(std::make_pair(event, size));
+ } else {
+ // Add to existing.
+ it->second += size;
+ }
+ ++counts_[event];
+}
+
+void LoggingStats::InsertGenericEvent(CastLoggingEvent event, int value) {
+ // Does this event belong to an existing event?
+ GenericStatsMap::iterator it = generic_stats_.find(event);
+ if (it == generic_stats_.end()) {
+ // New event.
+ start_time_[event] = clock_->NowTicks();
+ generic_stats_.insert(std::make_pair(event, value));
+ } else {
+ // Add to existing (will be used to compute average).
+ it->second += value;
+ }
+ ++counts_[event];
+}
+
+const FrameStatsMap* LoggingStats::GetFrameStatsData() {
+ // Compute framerate and bitrate (when available).
+ FrameStatsMap::iterator it;
+ for (it = frame_stats_.begin(); it != frame_stats_.end(); ++it) {
+ base::TimeDelta time_diff = clock_->NowTicks() - start_time_[it->first];
+ it->second->framerate_fps = counts_[it->first] / time_diff.InSecondsF();
+ if (it->second->bitrate_kbps > 0) {
+ it->second->bitrate_kbps = (8 / 1000) *
+ it->second->bitrate_kbps / time_diff.InSecondsF();
+ }
+ if (it->second->avg_delay_ms > 0)
+ it->second->avg_delay_ms /= counts_[it->first];
+ }
+ return &frame_stats_;
+}
+
+const PacketStatsMap* LoggingStats::GetPacketStatsData() {
+ PacketStatsMap::iterator it;
+ for (it = packet_stats_.begin(); it != packet_stats_.end(); ++it) {
+ if (counts_[it->first] == 0) continue;
+ base::TimeDelta time_diff = clock_->NowTicks() - start_time_[it->first];
+ it->second = (8 / 1000) * it->second / time_diff.InSecondsF();
+ }
+ return &packet_stats_;
+}
+
+const GenericStatsMap* LoggingStats::GetGenericStatsData() {
+ // Compute averages.
+ GenericStatsMap::iterator it;
+ for (it = generic_stats_.begin(); it != generic_stats_.end(); ++it) {
+ it->second /= counts_[ it->first];
+ }
+ return &generic_stats_;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/logging/logging_stats.h b/media/cast/logging/logging_stats.h
new file mode 100644
index 0000000000..65dccbd7b4
--- /dev/null
+++ b/media/cast/logging/logging_stats.h
@@ -0,0 +1,77 @@
+// Copyright 2013 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.
+
+#ifndef MEDIA_CAST_LOGGING_LOGGING_STATS_H_
+#define MEDIA_CAST_LOGGING_LOGGING_STATS_H_
+
+#include "base/basictypes.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/logging/logging_defines.h"
+
+namespace media {
+namespace cast {
+
+const int kNumberOfEvents = 19;
+
+class LoggingStats {
+ public:
+ explicit LoggingStats(base::TickClock* clock);
+
+ ~LoggingStats();
+
+ void Reset();
+
+ void InsertFrameEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id);
+
+ void InsertFrameEventWithSize(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ int frame_size);
+
+ void InsertFrameEventWithDelay(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ base::TimeDelta delay);
+
+ void InsertPacketEvent(CastLoggingEvent event,
+ uint32 rtp_timestamp,
+ uint8 frame_id,
+ uint16 packet_id,
+ uint16 max_packet_id,
+ int size);
+
+ void InsertGenericEvent(CastLoggingEvent event, int value);
+
+ // Get log stats: some of the values, such as frame rate and bit rates are
+ // computed at the time of the call.
+ const FrameStatsMap* GetFrameStatsData();
+
+ const PacketStatsMap* GetPacketStatsData();
+
+ const GenericStatsMap* GetGenericStatsData();
+
+ private:
+ void InsertBaseFrameEvent(CastLoggingEvent event,
+ uint8 frame_id,
+ uint32 rtp_timestamp);
+ FrameStatsMap frame_stats_;
+ PacketStatsMap packet_stats_;
+ GenericStatsMap generic_stats_;
+ // Every event has an individual start time
+ base::TimeTicks start_time_[kNumberOfEvents];
+ // Keep track of event counts.
+ int counts_[kNumberOfEvents];
+ base::TickClock* const clock_; // Not owned by this class.
+
+ DISALLOW_COPY_AND_ASSIGN(LoggingStats);
+ };
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_LOGGING_LOGGING_STATS_H_
+
diff --git a/media/cast/logging/logging_unittest.cc b/media/cast/logging/logging_unittest.cc
new file mode 100644
index 0000000000..ce55e65716
--- /dev/null
+++ b/media/cast/logging/logging_unittest.cc
@@ -0,0 +1,248 @@
+// Copyright 2013 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 <gtest/gtest.h>
+
+#include "base/rand_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/logging/logging_impl.h"
+
+
+namespace media {
+namespace cast {
+
+ // Insert frame duration- one second.
+const int64 kIntervalTime1S = 1;
+// Test frame rate goal - 30fps.
+const int kFrameIntervalMs = 33;
+
+static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+
+class TestLogging : public ::testing::Test {
+ protected:
+ TestLogging()
+ // Enable logging, disable tracing and uma.
+ : logging_(&testing_clock_, true, false, false) {
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ }
+
+ virtual ~TestLogging() {}
+
+ LoggingImpl logging_;
+ base::SimpleTestTickClock testing_clock_;
+};
+
+TEST_F(TestLogging, BasicFrameLogging) {
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint8 frame_id = 0;
+ do {
+ logging_.InsertFrameEvent(kAudioFrameCaptured, rtp_timestamp, frame_id);
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ FrameRawMap frame_map = logging_.GetFrameRawData();
+ // Size of map should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, frame_map.size());
+ // Verify stats.
+ const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
+ // Size of stats equals the number of events.
+ EXPECT_EQ(1u, frame_stats->size());
+ FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
+ EXPECT_TRUE(it != frame_stats->end());
+ EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
+ EXPECT_EQ(0, it->second->bitrate_kbps);
+ EXPECT_EQ(0, it->second->max_delay_ms);
+ EXPECT_EQ(0, it->second->min_delay_ms);
+ EXPECT_EQ(0, it->second->avg_delay_ms);
+}
+
+TEST_F(TestLogging, FrameLoggingWithSize) {
+ // Average packet size.
+ const int kBaseFrameSizeBytes = 25000;
+ const int kRandomSizeInterval = 100;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint8 frame_id = 0;
+ do {
+ int size = kBaseFrameSizeBytes +
+ base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
+ logging_.InsertFrameEventWithSize(
+ kAudioFrameCaptured, rtp_timestamp, frame_id, size);
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ FrameRawMap frame_map = logging_.GetFrameRawData();
+ // Size of map should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, frame_map.size());
+ // Verify stats.
+ const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
+ // Size of stats equals the number of events.
+ EXPECT_EQ(1u, frame_stats->size());
+ FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
+ EXPECT_TRUE(it != frame_stats->end());
+ EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
+ EXPECT_NEAR(8 * kBaseFrameSizeBytes / (kFrameIntervalMs * 1000),
+ it->second->bitrate_kbps, kRandomSizeInterval);
+ EXPECT_EQ(0, it->second->max_delay_ms);
+ EXPECT_EQ(0, it->second->min_delay_ms);
+ EXPECT_EQ(0, it->second->avg_delay_ms);
+}
+
+TEST_F(TestLogging, FrameLoggingWithDelay) {
+ // Average packet size.
+ const int kPlayoutDelayMs = 50;
+ const int kRandomSizeInterval = 20;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint8 frame_id = 0;
+ do {
+ int delay = kPlayoutDelayMs +
+ base::RandInt(-kRandomSizeInterval, kRandomSizeInterval);
+ logging_.InsertFrameEventWithDelay(
+ kAudioFrameCaptured, rtp_timestamp, frame_id,
+ base::TimeDelta::FromMilliseconds(delay));
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ FrameRawMap frame_map = logging_.GetFrameRawData();
+ // Size of map should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, frame_map.size());
+ // Verify stats.
+ const FrameStatsMap* frame_stats = logging_.GetFrameStatsData();
+ // Size of stats equals the number of events.
+ EXPECT_EQ(1u, frame_stats->size());
+ FrameStatsMap::const_iterator it = frame_stats->find(kAudioFrameCaptured);
+ EXPECT_TRUE(it != frame_stats->end());
+ EXPECT_NEAR(30.3, it->second->framerate_fps, 0.1);
+ EXPECT_EQ(0, it->second->bitrate_kbps);
+ EXPECT_GE(kPlayoutDelayMs + kRandomSizeInterval, it->second->max_delay_ms);
+ EXPECT_LE(kPlayoutDelayMs - kRandomSizeInterval, it->second->min_delay_ms);
+ EXPECT_NEAR(kPlayoutDelayMs, it->second->avg_delay_ms,
+ 0.2 * kRandomSizeInterval);
+}
+
+TEST_F(TestLogging, MultipleEventFrameLogging) {
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint8 frame_id = 0;
+ do {
+ logging_.InsertFrameEvent(kAudioFrameCaptured, rtp_timestamp, frame_id);
+ if (frame_id % 2) {
+ logging_.InsertFrameEventWithSize(
+ kAudioFrameEncoded, rtp_timestamp, frame_id, 1500);
+ } else if (frame_id % 3) {
+ logging_.InsertFrameEvent(kVideoFrameDecoded, rtp_timestamp, frame_id);
+ } else {
+ logging_.InsertFrameEventWithDelay(
+ kVideoRenderDelay, rtp_timestamp, frame_id,
+ base::TimeDelta::FromMilliseconds(20));
+ }
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ FrameRawMap frame_map = logging_.GetFrameRawData();
+ // Size of map should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, frame_map.size());
+ // Multiple events captured per frame.
+}
+
+TEST_F(TestLogging, PacketLogging) {
+ const int kNumPacketsPerFrame = 10;
+ const int kBaseSize = 2500;
+ const int kSizeInterval = 100;
+ base::TimeTicks start_time = testing_clock_.NowTicks();
+ base::TimeDelta time_interval = testing_clock_.NowTicks() - start_time;
+ uint32 rtp_timestamp = 0;
+ uint8 frame_id = 0;
+ do {
+ for (int i = 0; i < kNumPacketsPerFrame; ++i) {
+ int size = kBaseSize + base::RandInt(-kSizeInterval, kSizeInterval);
+ logging_.InsertPacketEvent(kPacketSentToPacer, rtp_timestamp, frame_id,
+ i, kNumPacketsPerFrame, size);
+ }
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kFrameIntervalMs));
+ rtp_timestamp += kFrameIntervalMs * 90;
+ ++frame_id;
+ time_interval = testing_clock_.NowTicks() - start_time;
+ } while (time_interval.InSeconds() < kIntervalTime1S);
+ // Get logging data.
+ PacketRawMap raw_map = logging_.GetPacketRawData();
+ // Size of map should be equal to the number of frames logged.
+ EXPECT_EQ(frame_id, raw_map.size());
+ // Verify stats.
+ const PacketStatsMap* stats_map = logging_.GetPacketStatsData();
+ // Size of stats equals the number of events.
+ EXPECT_EQ(1u, stats_map->size());
+ PacketStatsMap::const_iterator it = stats_map->find(kPacketSentToPacer);
+ EXPECT_TRUE(it != stats_map->end());
+ // We only store the bitrate as a packet statistic.
+ EXPECT_NEAR(8 * kNumPacketsPerFrame * kBaseSize / (kFrameIntervalMs * 1000),
+ it->second, kSizeInterval);
+}
+
+TEST_F(TestLogging, GenericLogging) {
+ // Insert multiple generic types.
+ const int kNumRuns = 1000;
+ const int kBaseValue = 20;
+ for (int i = 0; i < kNumRuns; ++i) {
+ int value = kBaseValue + base::RandInt(-5, 5);
+ logging_.InsertGenericEvent(kRtt, value);
+ if (i % 2) {
+ logging_.InsertGenericEvent(kPacketLoss, value);
+ }
+ if (!(i % 4)) {
+ logging_.InsertGenericEvent(kJitter, value);
+ }
+ }
+ GenericRawMap raw_map = logging_.GetGenericRawData();
+ const GenericStatsMap* stats_map = logging_.GetGenericStatsData();
+ // Size of generic map = number of different events.
+ EXPECT_EQ(3u, raw_map.size());
+ EXPECT_EQ(3u, stats_map->size());
+ // Raw events - size of internal map = number of calls.
+ GenericRawMap::iterator rit = raw_map.find(kRtt);
+ EXPECT_EQ(kNumRuns, rit->second.value.size());
+ EXPECT_EQ(kNumRuns, rit->second.timestamp.size());
+ rit = raw_map.find(kPacketLoss);
+ EXPECT_EQ(kNumRuns / 2, rit->second.value.size());
+ EXPECT_EQ(kNumRuns / 2, rit->second.timestamp.size());
+ rit = raw_map.find(kJitter);
+ EXPECT_EQ(kNumRuns / 4, rit->second.value.size());
+ EXPECT_EQ(kNumRuns / 4, rit->second.timestamp.size());
+ // Stats - one value per event.
+ GenericStatsMap::const_iterator sit = stats_map->find(kRtt);
+ EXPECT_NEAR(kBaseValue, sit->second, 2.5);
+ sit = stats_map->find(kPacketLoss);
+ EXPECT_NEAR(kBaseValue, sit->second, 2.5);
+ sit = stats_map->find(kJitter);
+ EXPECT_NEAR(kBaseValue, sit->second, 2.5);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtcp/rtcp.cc b/media/cast/rtcp/rtcp.cc
index 875736bb80..a1720ffe1a 100644
--- a/media/cast/rtcp/rtcp.cc
+++ b/media/cast/rtcp/rtcp.cc
@@ -273,7 +273,7 @@ void Rtcp::SendRtcp(const base::TimeTicks& now,
packet_type_flags |= RtcpSender::kRtcpRrtr;
RtcpReceiverReferenceTimeReport rrtr;
- ConvertTimeToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
+ ConvertTimeTicksToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
time_last_report_sent_ = now;
last_report_sent_ = ConvertToNtpDiff(rrtr.ntp_seconds, rrtr.ntp_fraction);
@@ -335,9 +335,9 @@ bool Rtcp::RtpTimestampInSenderTime(int frequency, uint32 rtp_timestamp,
// Sanity check.
if (abs(rtp_time_diff_ms) > kMaxDiffSinceReceivedRtcpMs) return false;
- *rtp_timestamp_in_ticks =
- ConvertNtpToTime(last_received_ntp_seconds_, last_received_ntp_fraction_) +
- base::TimeDelta::FromMilliseconds(rtp_time_diff_ms);
+ *rtp_timestamp_in_ticks = ConvertNtpToTimeTicks(last_received_ntp_seconds_,
+ last_received_ntp_fraction_) +
+ base::TimeDelta::FromMilliseconds(rtp_time_diff_ms);
return true;
}
diff --git a/media/cast/rtcp/rtcp_unittest.cc b/media/cast/rtcp/rtcp_unittest.cc
index a4213ff605..ccbdcac863 100644
--- a/media/cast/rtcp/rtcp_unittest.cc
+++ b/media/cast/rtcp/rtcp_unittest.cc
@@ -113,8 +113,8 @@ class RtcpTest : public ::testing::Test {
};
TEST_F(RtcpTest, TimeToSend) {
- base::TimeTicks start_time =
- base::TimeTicks::FromInternalValue(kStartMillisecond * 1000);
+ base::TimeTicks start_time;
+ start_time += base::TimeDelta::FromMilliseconds(kStartMillisecond);
Rtcp rtcp(&testing_clock_,
&mock_sender_feedback_,
&transport_,
@@ -293,29 +293,40 @@ TEST_F(RtcpTest, Rtt) {
EXPECT_NEAR((2 * kAddedShortDelay + 2 * kAddedDelay) / 2,
avg_rtt.InMilliseconds(),
1);
- EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 1);
+ EXPECT_NEAR(2 * kAddedShortDelay, min_rtt.InMilliseconds(), 1);
EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
}
TEST_F(RtcpTest, NtpAndTime) {
- RtcpPeer rtcp_peer(&testing_clock_,
- &mock_sender_feedback_,
- NULL,
- NULL,
- NULL,
- kRtcpReducedSize,
- base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
- false,
- kReceiverSsrc,
- kCName);
- uint32 ntp_seconds = 0;
- uint32 ntp_fractions = 0;
- base::TimeTicks input_time = base::TimeTicks::FromInternalValue(
- 12345678901000LL + kNtpEpochDeltaMicroseconds);
- ConvertTimeToNtp(input_time, &ntp_seconds, &ntp_fractions);
- EXPECT_EQ(12345678u, ntp_seconds);
- EXPECT_EQ(input_time,
- ConvertNtpToTime(ntp_seconds, ntp_fractions));
+ const int64 kSecondsbetweenYear1900and2010 = GG_INT64_C(40176 * 24 * 60 * 60);
+ const int64 kSecondsbetweenYear1900and2030 = GG_INT64_C(47481 * 24 * 60 * 60);
+
+ uint32 ntp_seconds_1 = 0;
+ uint32 ntp_fractions_1 = 0;
+ base::TimeTicks input_time = base::TimeTicks::Now();
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds_1, &ntp_fractions_1);
+
+ // Verify absolute value.
+ EXPECT_GT(ntp_seconds_1, kSecondsbetweenYear1900and2010);
+ EXPECT_LT(ntp_seconds_1, kSecondsbetweenYear1900and2030);
+
+ base::TimeTicks out_1 = ConvertNtpToTimeTicks(ntp_seconds_1, ntp_fractions_1);
+ EXPECT_EQ(input_time, out_1); // Verify inverse.
+
+ base::TimeDelta time_delta = base::TimeDelta::FromMilliseconds(1100);
+ input_time += time_delta;
+
+ uint32 ntp_seconds_2 = 0;
+ uint32 ntp_fractions_2 = 0;
+
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds_2, &ntp_fractions_2);
+ base::TimeTicks out_2 = ConvertNtpToTimeTicks(ntp_seconds_2, ntp_fractions_2);
+ EXPECT_EQ(input_time, out_2); // Verify inverse.
+
+ // Verify delta.
+ EXPECT_EQ((out_2 - out_1), time_delta);
+ EXPECT_EQ((ntp_seconds_2 - ntp_seconds_1), GG_UINT32_C(1));
+ EXPECT_NEAR((ntp_fractions_2 - ntp_fractions_1), 0xffffffff / 10, 1);
}
TEST_F(RtcpTest, WrapAround) {
@@ -367,34 +378,35 @@ TEST_F(RtcpTest, RtpTimestampInSenderTime) {
uint32 ntp_seconds = 0;
uint32 ntp_fractions = 0;
- base::TimeTicks input_time = base::TimeTicks::FromInternalValue(
- 12345678901000LL + kNtpEpochDeltaMicroseconds);
+ uint64 input_time_us = 12345678901000LL;
+ base::TimeTicks input_time;
+ input_time += base::TimeDelta::FromMicroseconds(input_time_us);
// Test exact match.
- ConvertTimeToNtp(input_time, &ntp_seconds, &ntp_fractions);
+ ConvertTimeTicksToNtp(input_time, &ntp_seconds, &ntp_fractions);
rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
+ &rtp_timestamp_in_ticks));
EXPECT_EQ(input_time, rtp_timestamp_in_ticks);
// Test older rtp_timestamp.
rtp_timestamp = 32000;
EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
+ &rtp_timestamp_in_ticks));
EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(1000),
rtp_timestamp_in_ticks);
// Test older rtp_timestamp with wrap.
rtp_timestamp = 4294903296u;
EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
+ &rtp_timestamp_in_ticks));
EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(4000),
rtp_timestamp_in_ticks);
// Test newer rtp_timestamp.
rtp_timestamp = 128000;
EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
+ &rtp_timestamp_in_ticks));
EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(2000),
rtp_timestamp_in_ticks);
@@ -403,7 +415,7 @@ TEST_F(RtcpTest, RtpTimestampInSenderTime) {
rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
rtp_timestamp = 64000;
EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
- &rtp_timestamp_in_ticks));
+ &rtp_timestamp_in_ticks));
EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(4000),
rtp_timestamp_in_ticks);
}
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
index b5dd4d7f3c..c4cb42e809 100644
--- a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
@@ -23,7 +23,6 @@ static const int kMaxPacketLength = 1500;
static const int kSsrc = 0x12345;
static const unsigned int kFrameSize = 5000;
static const int kMaxPacketStorageTimeMs = 300;
-static const int64 kStartMillisecond = 0;
class TestRtpPacketTransport : public PacedPacketSender {
public:
@@ -108,8 +107,6 @@ class RtpPacketizerTest : public ::testing::Test {
config_.payload_type = kPayload;
config_.max_payload_length = kMaxPacketLength;
transport_.reset(new TestRtpPacketTransport(config_));
- testing_clock_.Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
rtp_packetizer_.reset(
new RtpPacketizer(transport_.get(), &packet_storage_, config_));
}
diff --git a/media/cast/rtp_sender/rtp_sender.cc b/media/cast/rtp_sender/rtp_sender.cc
index d306c6e7aa..d06a503dc8 100644
--- a/media/cast/rtp_sender/rtp_sender.cc
+++ b/media/cast/rtp_sender/rtp_sender.cc
@@ -121,7 +121,7 @@ void RtpSender::RtpStatistics(const base::TimeTicks& now,
// was captured.
uint32 ntp_seconds = 0;
uint32 ntp_fraction = 0;
- ConvertTimeToNtp(now, &ntp_seconds, &ntp_fraction);
+ ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction);
sender_info->ntp_seconds = ntp_seconds;
sender_info->ntp_fraction = ntp_fraction;
diff --git a/media/cast/test/encode_decode_test.cc b/media/cast/test/encode_decode_test.cc
index 0bcd6c2633..bc2c0a1725 100644
--- a/media/cast/test/encode_decode_test.cc
+++ b/media/cast/test/encode_decode_test.cc
@@ -77,6 +77,7 @@ TEST_F(EncodeDecodeTest, BasicEncodeDecode) {
I420VideoFrame decoded_frame;
// Encode frame.
encoder_->Encode(*(video_frame_.get()), &encoded_frame);
+ EXPECT_GT(encoded_frame.data.size(), GG_UINT64_C(0));
// Decode frame.
decoder_->Decode(encoded_frame, &decoded_frame);
// Validate data.
diff --git a/media/cast/test/end2end_unittest.cc b/media/cast/test/end2end_unittest.cc
index 3caa6e74bf..b465f2096a 100644
--- a/media/cast/test/end2end_unittest.cc
+++ b/media/cast/test/end2end_unittest.cc
@@ -27,9 +27,7 @@
namespace media {
namespace cast {
-// Since our time is based on year 1600 and NTP is based on year 1900 we must
-// initialize our fake clock to at least 300 year passed year 1600.
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
+static const int64 kStartMillisecond = GG_INT64_C(1245);
static const int kAudioChannels = 2;
static const int kAudioSamplingFrequency = 48000;
static const int kSoundFrequency = 1234; // Frequency of sinusoid wave.
@@ -676,7 +674,10 @@ TEST_F(End2EndTest, LoopNoLossOpus) {
}
// This tests start sending audio and video before the receiver is ready.
-TEST_F(End2EndTest, StartSenderBeforeReceiver) {
+//
+// TODO(miu): Test disabled because of non-determinism.
+// http://crbug.com/314233
+TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) {
SetupConfig(kOpus, kAudioSamplingFrequency, false, 1);
Create();
test_receiver_audio_callback_->SetExpectedResult(