diff options
author | imcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-21 03:29:41 +0000 |
---|---|---|
committer | imcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-21 03:29:41 +0000 |
commit | da76b9e0cce03456acba6e45689804d3bf6c6e83 (patch) | |
tree | d7bf73a75ea3033ea3ea56ea0bf3baf0b0d38585 /media | |
parent | b0d594d00c6fb0befa6803496ba4125327e8c990 (diff) | |
download | chromium_src-da76b9e0cce03456acba6e45689804d3bf6c6e83.zip chromium_src-da76b9e0cce03456acba6e45689804d3bf6c6e83.tar.gz chromium_src-da76b9e0cce03456acba6e45689804d3bf6c6e83.tar.bz2 |
Cast: Added a LogSerializer class and hook it up to sender app.
A LogSerializer takes a FrameEventMap and PacketEventMap as well as a stream id associated with these events and outputs them in a serialized format. A LogSerializer is capable of taking more than one set of data (e.g. one set of audio + one set of video frame/packet events) before outputting the serialized data.
The intended use case is that at the end of a mirroring session, logged events will be passed to LogSerializer in order to obtain a serialized format to return to the application which can then be uploaded.
Review URL: https://codereview.chromium.org/166273010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252483 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/cast/cast.gyp | 6 | ||||
-rw-r--r-- | media/cast/logging/log_serializer.cc | 103 | ||||
-rw-r--r-- | media/cast/logging/log_serializer.h | 70 | ||||
-rw-r--r-- | media/cast/test/receiver.cc | 24 | ||||
-rw-r--r-- | media/cast/test/sender.cc | 134 |
5 files changed, 316 insertions, 21 deletions
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp index e1a57dc..cc8288f 100644 --- a/media/cast/cast.gyp +++ b/media/cast/cast.gyp @@ -60,9 +60,14 @@ 'cast_logging_proto_lib', '<(DEPTH)/base/base.gyp:base', ], + 'export_dependent_settings': [ + 'cast_logging_proto_lib', + ], 'sources': [ 'logging/encoding_event_subscriber.cc', 'logging/encoding_event_subscriber.h', + 'logging/log_serializer.cc', + 'logging/log_serializer.h', ], # source }, ], # targets, @@ -150,6 +155,7 @@ ], 'dependencies': [ 'cast_config', + 'sender_logging', '<(DEPTH)/ui/gfx/gfx.gyp:gfx', '<(DEPTH)/net/net.gyp:net_test_support', '<(DEPTH)/media/cast/cast_sender.gyp:*', diff --git a/media/cast/logging/log_serializer.cc b/media/cast/logging/log_serializer.cc new file mode 100644 index 0000000..bc6a1ae --- /dev/null +++ b/media/cast/logging/log_serializer.cc @@ -0,0 +1,103 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/cast/logging/log_serializer.h" + +#include "net/base/big_endian.h" + +namespace media { +namespace cast { + +LogSerializer::LogSerializer(const int max_serialized_bytes) + : index_so_far_(0), max_serialized_bytes_(max_serialized_bytes) { + DCHECK_GT(max_serialized_bytes_, 0); +} + +LogSerializer::~LogSerializer() {} + +// The format is as follows: +// 32-bit integer describing |stream_id|. +// 32-bit integer describing |first_rtp_timestamp|. +// 32-bit integer describing number of frame events. +// (The following repeated for number of frame events): +// 16-bit integer describing the following AggregatedFrameEvent proto size +// in bytes. +// The AggregatedFrameEvent proto. +// 32-bit integer describing number of packet events. +// (The following repeated for number of packet events): +// 16-bit integer describing the following AggregatedPacketEvent proto +// size in bytes. +// The AggregatedPacketEvent proto. +bool LogSerializer::SerializeEventsForStream( + const int stream_id, + const FrameEventMap& frame_events, + const PacketEventMap& packet_events, + const RtpTimestamp first_rtp_timestamp) { + if (!serialized_log_so_far_) { + serialized_log_so_far_.reset(new std::string(max_serialized_bytes_, 0)); + } + + int remaining_space = serialized_log_so_far_->size() - index_so_far_; + if (remaining_space <= 0) + return false; + + net::BigEndianWriter writer(&(*serialized_log_so_far_)[index_so_far_], + remaining_space); + + // Write stream ID. + if (!writer.WriteU32(stream_id)) + return false; + + // Write first RTP timestamp. + if (!writer.WriteU32(first_rtp_timestamp)) + return false; + + // Frame events - write size first, then write entries. + if (!writer.WriteU32(frame_events.size())) + return false; + + for (media::cast::FrameEventMap::const_iterator it = frame_events.begin(); + it != frame_events.end(); + ++it) { + int proto_size = it->second->ByteSize(); + + // Write size of the proto, then write the proto. + if (!writer.WriteU16(proto_size)) + return false; + if (!it->second->SerializeToArray(writer.ptr(), writer.remaining())) + return false; + if (!writer.Skip(proto_size)) + return false; + } + + // Write packet events. + if (!writer.WriteU32(packet_events.size())) + return false; + for (media::cast::PacketEventMap::const_iterator it = packet_events.begin(); + it != packet_events.end(); + ++it) { + int proto_size = it->second->ByteSize(); + // Write size of the proto, then write the proto. + if (!writer.WriteU16(proto_size)) + return false; + if (!it->second->SerializeToArray(writer.ptr(), writer.remaining())) + return false; + if (!writer.Skip(proto_size)) + return false; + } + + index_so_far_ = serialized_log_so_far_->size() - writer.remaining(); + return true; +} + +scoped_ptr<std::string> LogSerializer::GetSerializedLogAndReset() { + serialized_log_so_far_->resize(index_so_far_); + index_so_far_ = 0; + return serialized_log_so_far_.Pass(); +} + +int LogSerializer::GetSerializedLength() const { return index_so_far_; } + +} // namespace cast +} // namespace media diff --git a/media/cast/logging/log_serializer.h b/media/cast/logging/log_serializer.h new file mode 100644 index 0000000..679cce1 --- /dev/null +++ b/media/cast/logging/log_serializer.h @@ -0,0 +1,70 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CAST_LOGGING_LOG_SERIALIZER_H_ +#define MEDIA_CAST_LOGGING_LOG_SERIALIZER_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "media/cast/logging/encoding_event_subscriber.h" + +namespace media { +namespace cast { + +// The max allowed size of serialized log. +const int kMaxSerializedLogBytes = 20 * 1000 * 1000; + +// This class takes FrameEventMap and PacketEventMap returned from an +// EncodingEventSubscriber and serialize them. An instance of this class +// can take input from multiple EncodingEventSubscribers (which represents +// different streams). +// Usage: +// 1. Construct a LogSerializer with a predefined maximum serialized length. +// 2. For each set of FrameEventMap and PacketEventMap returned by +// EncodingEventSubscriber installed on each stream, call +// |SerializeEventsForStream()| along with the stream id. Check the returned +// value to see if serialization succeeded. +// 3. Call |GetSerializedLogAndReset()| to get the result. +// 4. (Optional) Call |GetSerializedLengthSoFar()| between each serialize call. +class LogSerializer { + public: + // Constructs a LogSerializer that caps size of serialized message to + // |max_serialized_bytes|. + LogSerializer(const int max_serialized_bytes); + ~LogSerializer(); + + // Serialize |frame_events|, |packet_events|, |first_rtp_timestamp| + // returned from EncodingEventSubscriber associated with |stream_id|. + // + // Returns |true| if serialization is successful. This function + // returns |false| if the serialized string will exceed |kMaxSerializedsize|. + // + // This may be called multiple times with different streams and events before + // calling |GetSerializedString()|. + // + // See .cc file for format specification. + bool SerializeEventsForStream(const int stream_id, + const FrameEventMap& frame_events, + const PacketEventMap& packet_events, + const RtpTimestamp first_rtp_timestamp); + + // Gets a string of serialized events up to the last successful call to + // |SerializeEventsForStream()| and resets it. + scoped_ptr<std::string> GetSerializedLogAndReset(); + + // Returns the length of the serialized string since last + // successful serialization / reset. + int GetSerializedLength() const; + + private: + scoped_ptr<std::string> serialized_log_so_far_; + int index_so_far_; + const int max_serialized_bytes_; +}; + +} // namespace cast +} // namespace media + +#endif // MEDIA_CAST_LOGGING_LOG_SERIALIZER_H_ diff --git a/media/cast/test/receiver.cc b/media/cast/test/receiver.cc index fb8604c..34a243f 100644 --- a/media/cast/test/receiver.cc +++ b/media/cast/test/receiver.cc @@ -233,18 +233,20 @@ int main(int argc, char** argv) { scoped_ptr<base::TickClock> clock(new base::DefaultTickClock()); - // Enable receiver side threads, and disable logging. - // Running transport on main thread. + // Enable main and receiver side threads only. Enable raw event logging. + // Running transport on the main thread. + media::cast::CastLoggingConfig logging_config; + logging_config.enable_raw_data_collection = true; + scoped_refptr<media::cast::CastEnvironment> cast_environment( - new media::cast::CastEnvironment( - clock.Pass(), - main_message_loop.message_loop_proxy(), - NULL, - audio_thread.message_loop_proxy(), - NULL, - video_thread.message_loop_proxy(), - main_message_loop.message_loop_proxy(), - media::cast::GetDefaultCastReceiverLoggingConfig())); + new media::cast::CastEnvironment(clock.Pass(), + main_message_loop.message_loop_proxy(), + NULL, + audio_thread.message_loop_proxy(), + NULL, + video_thread.message_loop_proxy(), + main_message_loop.message_loop_proxy(), + logging_config)); media::cast::AudioReceiverConfig audio_config = media::cast::GetAudioReceiverConfig(); diff --git a/media/cast/test/sender.cc b/media/cast/test/sender.cc index 603b2de..4f94a44 100644 --- a/media/cast/test/sender.cc +++ b/media/cast/test/sender.cc @@ -6,6 +6,7 @@ // or read from a file. #include "base/at_exit.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/threading/thread.h" @@ -14,13 +15,17 @@ #include "media/cast/cast_config.h" #include "media/cast/cast_environment.h" #include "media/cast/cast_sender.h" +#include "media/cast/logging/encoding_event_subscriber.h" +#include "media/cast/logging/log_serializer.h" #include "media/cast/logging/logging_defines.h" +#include "media/cast/logging/proto/raw_events.pb.h" #include "media/cast/test/utility/audio_utility.h" #include "media/cast/test/utility/input_builder.h" #include "media/cast/test/utility/video_utility.h" #include "media/cast/transport/cast_transport_defines.h" #include "media/cast/transport/cast_transport_sender.h" #include "media/cast/transport/transport/udp_transport.h" +#include "net/base/big_endian.h" #include "ui/gfx/size.h" namespace media { @@ -41,6 +46,8 @@ namespace cast { #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" +#define DEFAULT_LOGGING_DURATION "30" + namespace { static const int kAudioChannels = 2; static const int kAudioSamplingFrequency = 48000; @@ -72,6 +79,21 @@ std::string GetIpAddress(const std::string display_text) { return ip_address; } +int GetLoggingDuration() { + test::InputBuilder input( + "Choose logging duration (seconds), 0 for no logging.", + DEFAULT_LOGGING_DURATION, + 0, + INT_MAX); + return input.GetIntInput(); +} + +std::string GetLogFileDestination() { + test::InputBuilder input( + "Enter log file destination.", "./raw_events.log", INT_MIN, INT_MAX); + return input.GetStringInput(); +} + bool ReadFromFile() { test::InputBuilder input( "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); @@ -307,6 +329,64 @@ net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { return net::IPEndPoint(ip_number, port); } +void WriteLogsToFileAndStopSubscribing( + const scoped_refptr<media::cast::CastEnvironment>& cast_environment, + scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber, + scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber, + file_util::ScopedFILE log_file) { + media::cast::LogSerializer serializer(media::cast::kMaxSerializedLogBytes); + + // Serialize video events. + cast_environment->Logging()->RemoveRawEventSubscriber( + video_event_subscriber.get()); + media::cast::FrameEventMap frame_events; + media::cast::PacketEventMap packet_events; + media::cast::RtpTimestamp first_rtp_timestamp; + video_event_subscriber->GetEventsAndReset(&frame_events, &packet_events, + &first_rtp_timestamp); + + VLOG(0) << "Video frame map size: " << frame_events.size(); + VLOG(0) << "Video packet map size: " << packet_events.size(); + + // "Assign" stream id 0 for the video events. + if (!serializer.SerializeEventsForStream(0, frame_events, packet_events, + first_rtp_timestamp)) { + VLOG(1) << "Failed to serialize video events."; + return; + } + + int length_so_far = serializer.GetSerializedLength(); + VLOG(0) << "Video events serialized length: " << length_so_far; + + // Serialize audio events. + cast_environment->Logging()->RemoveRawEventSubscriber( + audio_event_subscriber.get()); + audio_event_subscriber->GetEventsAndReset(&frame_events, &packet_events, + &first_rtp_timestamp); + + VLOG(0) << "Audio frame map size: " << frame_events.size(); + VLOG(0) << "Audio packet map size: " << packet_events.size(); + + // "Assign" stream id 1 for the audio events. + if (!serializer.SerializeEventsForStream(1, frame_events, packet_events, + first_rtp_timestamp)) { + VLOG(1) << "Failed to serialize audio events."; + return; + } + + VLOG(0) << "Audio events serialized length: " + << serializer.GetSerializedLength() - length_so_far; + + scoped_ptr<std::string> serialized_string = + serializer.GetSerializedLogAndReset(); + VLOG(0) << "Serialized string size: " << serialized_string->size(); + + size_t ret = fwrite( + &(*serialized_string)[0], 1, serialized_string->size(), log_file.get()); + if (ret != serialized_string->size()) + VLOG(1) << "Failed to write logs to file."; +} + } // namespace int main(int argc, char** argv) { @@ -349,18 +429,20 @@ int main(int argc, char** argv) { base::Bind(&UpdateCastTransportStatus), io_message_loop.message_loop_proxy())); - // Enable main and send side threads only. Disable logging. + // Enable main and send side threads only. Enable raw event logging. // Running transport on the main thread. + media::cast::CastLoggingConfig logging_config; + logging_config.enable_raw_data_collection = true; + scoped_refptr<media::cast::CastEnvironment> cast_environment( - new media::cast::CastEnvironment( - clock.Pass(), - io_message_loop.message_loop_proxy(), - audio_thread.message_loop_proxy(), - NULL, - video_thread.message_loop_proxy(), - NULL, - io_message_loop.message_loop_proxy(), - media::cast::GetDefaultCastSenderLoggingConfig())); + new media::cast::CastEnvironment(clock.Pass(), + io_message_loop.message_loop_proxy(), + audio_thread.message_loop_proxy(), + NULL, + video_thread.message_loop_proxy(), + NULL, + io_message_loop.message_loop_proxy(), + logging_config)); scoped_ptr<media::cast::CastSender> cast_sender( media::cast::CastSender::CreateCastSender( @@ -380,11 +462,43 @@ int main(int argc, char** argv) { video_config, frame_input)); + // Set up event subscribers. + // TODO(imcheng): Set up separate subscribers for audio / video / other. + int logging_duration = media::cast::GetLoggingDuration(); + scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber; + scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber; + if (logging_duration > 0) { + std::string log_file_name(media::cast::GetLogFileDestination()); + video_event_subscriber.reset(new media::cast::EncodingEventSubscriber( + media::cast::VIDEO_EVENT, 10000)); + audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber( + media::cast::AUDIO_EVENT, 10000)); + cast_environment->Logging()->AddRawEventSubscriber( + video_event_subscriber.get()); + cast_environment->Logging()->AddRawEventSubscriber( + audio_event_subscriber.get()); + file_util::ScopedFILE log_file(fopen(log_file_name.c_str(), "w")); + if (!log_file) { + VLOG(1) << "Failed to open log file for writing."; + exit(-1); + } + + io_message_loop.message_loop_proxy()->PostDelayedTask( + FROM_HERE, + base::Bind(&WriteLogsToFileAndStopSubscribing, + cast_environment, + base::Passed(&video_event_subscriber), + base::Passed(&audio_event_subscriber), + base::Passed(&log_file)), + base::TimeDelta::FromSeconds(logging_duration)); + } + test_thread.message_loop_proxy()->PostTask( FROM_HERE, base::Bind(&media::cast::SendProcess::SendFrame, base::Unretained(send_process.get()))); io_message_loop.Run(); + return 0; } |