Chromium Code Reviews| Index: media/cast/test/sender.cc |
| diff --git a/media/cast/test/sender.cc b/media/cast/test/sender.cc |
| index 32c0e3765519f5f60e700d598fd8a2fa6710b735..51172ce8cef8e8d37fedf06f7a5bb6bffc370e8e 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,7 +15,9 @@ |
| #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/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" |
| @@ -23,6 +26,14 @@ |
| #include "media/cast/transport/transport/udp_transport.h" |
| #include "ui/gfx/size.h" |
| +#if defined(USE_SYSTEM_PROTOBUF) |
| +#include <google/protobuf/io/coded_stream.h> |
| +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| +#else |
| +#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h" |
| +#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h" |
| +#endif |
| + |
| namespace media { |
| namespace cast { |
| // Settings chosen to match default receiver settings. |
| @@ -42,6 +53,8 @@ namespace cast { |
| #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" |
| #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" |
| +#define DEFAULT_LOGGING_DURATION "300" |
| + |
| namespace { |
| static const int kAudioChannels = 2; |
| static const int kAudioSamplingFrequency = 48000; |
| @@ -77,6 +90,20 @@ 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.", |
| + "/tmp/raw_events.log", INT_MIN, INT_MAX); |
|
Alpha Left Google
2014/02/13 20:30:58
Save the log to current dir is better.
imcheng
2014/02/13 21:49:26
Done.
|
| + return input.GetStringInput(); |
| +} |
| + |
| bool ReadFromFile() { |
| test::InputBuilder input( |
| "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); |
| @@ -312,6 +339,95 @@ net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { |
| return net::IPEndPoint(ip_number, port); |
| } |
| +// TODO(imcheng): Extract this function to a class. |
|
Alpha Left Google
2014/02/13 20:30:58
No need for this TODO.
imcheng
2014/02/13 21:49:26
I want to do extract this to a class though so tha
|
| +void WriteTo(const media::cast::FrameEventMap& frame_events, |
| + const media::cast::PacketEventMap& packet_events, |
| + const media::cast::GenericEventMap& generic_events, |
| + std::string* output_string) { |
| + google::protobuf::io::StringOutputStream string_output_stream(output_string); |
| + |
| + google::protobuf::io::CodedOutputStream output_stream(&string_output_stream); |
| + |
| + int byte_count = 0; |
| + int last_byte_count = 0; |
| + // Frame events - write size first, then write entries |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(frame_events.size()); |
| + 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 |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(proto_size); |
| + |
| + bool success = it->second->SerializeToCodedStream(&output_stream); |
| + DCHECK(success); |
| + } |
| + |
| + byte_count = output_stream.ByteCount(); |
| + printf("Encoded frame events byte count: %d\n", byte_count - last_byte_count); |
| + last_byte_count = byte_count; |
| + |
| + // Write packet events |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(packet_events.size()); |
| + 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 |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(proto_size); |
| + |
| + bool success = it->second->SerializeToCodedStream(&output_stream); |
| + DCHECK(success); |
| + } |
| + |
| + byte_count = output_stream.ByteCount(); |
| + printf("Encoded packet events byte count: %d\n", |
| + byte_count - last_byte_count); |
| + last_byte_count = byte_count; |
| + |
| + // Write generic events |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(generic_events.size()); |
| + for (media::cast::GenericEventMap::const_iterator it = generic_events.begin(); |
| + it != generic_events.end(); ++it) { |
| + // Write size of the proto, then write the proto |
|
Alpha Left Google
2014/02/13 20:30:58
nit: period at the end of sentence.
imcheng
2014/02/13 21:49:26
Done.
|
| + output_stream.WriteLittleEndian32(it->second->ByteSize()); |
| + |
| + bool success = it->second->SerializeToCodedStream(&output_stream); |
| + DCHECK(success); |
| + } |
| + |
| + byte_count = output_stream.ByteCount(); |
| + printf("Encoded generic events byte count: %d\n", |
| + byte_count - last_byte_count); |
| + last_byte_count = byte_count; |
| +} |
| + |
| +void WriteLogsToFileAndStopSubscribing( |
| + const scoped_refptr<media::cast::CastEnvironment>& cast_environment, |
| + scoped_ptr<media::cast::EncodingEventSubscriber> subscriber, |
| + file_util::ScopedFILE log_file) { |
| + cast_environment->Logging()->RemoveRawEventSubscriber(subscriber.get()); |
| + media::cast::FrameEventMap frame_events; |
| + media::cast::PacketEventMap packet_events; |
| + media::cast::GenericEventMap generic_events; |
| + subscriber->GetFrameEventsAndReset(&frame_events); |
| + subscriber->GetPacketEventsAndReset(&packet_events); |
| + subscriber->GetGenericEventsAndReset(&generic_events); |
| + |
| + // TODO(imcheng): Consider sending the maps to another thread for processing |
|
Alpha Left Google
2014/02/13 20:30:58
There's no need for this. This is a test app any w
imcheng
2014/02/13 21:49:26
Done.
|
| + // so that it won't block the main thread. |
| + printf("Frame map size: %lu\n", frame_events.size()); |
| + printf("Packet map size: %lu\n", packet_events.size()); |
| + printf("Generic map size: %lu\n", generic_events.size()); |
| + |
| + std::string serialized_string; |
| + WriteTo(frame_events, packet_events, generic_events, &serialized_string); |
| + |
| + 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) { |
| @@ -355,8 +471,11 @@ 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(true); |
| + logging_config.enable_raw_data_collection = true; |
| + |
| scoped_refptr<media::cast::CastEnvironment> cast_environment( |
| new media::cast::CastEnvironment( |
| clock.Pass(), |
| @@ -366,7 +485,7 @@ int main(int argc, char** argv) { |
| video_thread.message_loop_proxy(), |
| NULL, |
| io_message_loop.message_loop_proxy(), |
| - media::cast::GetDefaultCastSenderLoggingConfig())); |
| + logging_config)); |
| scoped_ptr<media::cast::CastSender> cast_sender( |
| media::cast::CastSender::CreateCastSender( |
| @@ -386,11 +505,34 @@ 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> event_subscriber; |
| + if (logging_duration > 0) { |
| + std::string log_file_name(media::cast::GetLogFileDestination()); |
| + event_subscriber.reset(new media::cast::EncodingEventSubscriber); |
| + cast_environment->Logging()->AddRawEventSubscriber(event_subscriber.get()); |
| + file_util::ScopedFILE log_file(fopen(log_file_name.c_str(), "w")); |
| + if (!log_file) { |
| + printf("Failed to open log file for writing.\n"); |
| + exit(-1); |
| + } |
| + |
| + io_message_loop.message_loop_proxy()->PostDelayedTask(FROM_HERE, |
| + base::Bind(&WriteLogsToFileAndStopSubscribing, |
| + cast_environment, |
| + base::Passed(&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; |
| } |