diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-11-06 12:27:47 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-11-06 12:27:47 +0000 |
commit | 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740 (patch) | |
tree | 08e3fb2fdca3674ceb4d6cf527cb65e755cd993e /media/cast | |
parent | 0bdaf95291fc46702f274f40e8e5081e9ef23011 (diff) | |
download | chromium_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.cc | 26 | ||||
-rw-r--r-- | media/cast/audio_receiver/audio_receiver.h | 6 | ||||
-rw-r--r-- | media/cast/audio_receiver/audio_receiver_unittest.cc | 5 | ||||
-rw-r--r-- | media/cast/cast_defines.h | 41 | ||||
-rw-r--r-- | media/cast/logging/logging.gyp | 11 | ||||
-rw-r--r-- | media/cast/logging/logging_defines.cc | 79 | ||||
-rw-r--r-- | media/cast/logging/logging_defines.h | 111 | ||||
-rw-r--r-- | media/cast/logging/logging_impl.cc | 196 | ||||
-rw-r--r-- | media/cast/logging/logging_impl.h | 72 | ||||
-rw-r--r-- | media/cast/logging/logging_raw.cc | 139 | ||||
-rw-r--r-- | media/cast/logging/logging_raw.h | 85 | ||||
-rw-r--r-- | media/cast/logging/logging_stats.cc | 149 | ||||
-rw-r--r-- | media/cast/logging/logging_stats.h | 77 | ||||
-rw-r--r-- | media/cast/logging/logging_unittest.cc | 248 | ||||
-rw-r--r-- | media/cast/rtcp/rtcp.cc | 8 | ||||
-rw-r--r-- | media/cast/rtcp/rtcp_unittest.cc | 70 | ||||
-rw-r--r-- | media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc | 3 | ||||
-rw-r--r-- | media/cast/rtp_sender/rtp_sender.cc | 2 | ||||
-rw-r--r-- | media/cast/test/encode_decode_test.cc | 1 | ||||
-rw-r--r-- | media/cast/test/end2end_unittest.cc | 9 |
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( |