summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorimcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-21 03:29:41 +0000
committerimcheng@chromium.org <imcheng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-21 03:29:41 +0000
commitda76b9e0cce03456acba6e45689804d3bf6c6e83 (patch)
treed7bf73a75ea3033ea3ea56ea0bf3baf0b0d38585 /media
parentb0d594d00c6fb0befa6803496ba4125327e8c990 (diff)
downloadchromium_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.gyp6
-rw-r--r--media/cast/logging/log_serializer.cc103
-rw-r--r--media/cast/logging/log_serializer.h70
-rw-r--r--media/cast/test/receiver.cc24
-rw-r--r--media/cast/test/sender.cc134
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;
}