Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Test application that simulates a cast sender - Data can be either generated | 5 // Test application that simulates a cast sender - Data can be either generated |
| 6 // or read from a file. | 6 // or read from a file. |
| 7 | 7 |
| 8 #include "base/at_exit.h" | 8 #include "base/at_exit.h" |
| 9 #include "base/file_util.h" | |
| 9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 10 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/threading/thread.h" | 12 #include "base/threading/thread.h" |
| 12 #include "base/time/default_tick_clock.h" | 13 #include "base/time/default_tick_clock.h" |
| 13 #include "media/base/video_frame.h" | 14 #include "media/base/video_frame.h" |
| 14 #include "media/cast/cast_config.h" | 15 #include "media/cast/cast_config.h" |
| 15 #include "media/cast/cast_environment.h" | 16 #include "media/cast/cast_environment.h" |
| 16 #include "media/cast/cast_sender.h" | 17 #include "media/cast/cast_sender.h" |
| 18 #include "media/cast/logging/encoding_event_subscriber.h" | |
| 17 #include "media/cast/logging/logging_defines.h" | 19 #include "media/cast/logging/logging_defines.h" |
| 20 #include "media/cast/logging/proto/raw_events.pb.h" | |
| 18 #include "media/cast/test/utility/audio_utility.h" | 21 #include "media/cast/test/utility/audio_utility.h" |
| 19 #include "media/cast/test/utility/input_builder.h" | 22 #include "media/cast/test/utility/input_builder.h" |
| 20 #include "media/cast/test/utility/video_utility.h" | 23 #include "media/cast/test/utility/video_utility.h" |
| 21 #include "media/cast/transport/cast_transport_defines.h" | 24 #include "media/cast/transport/cast_transport_defines.h" |
| 22 #include "media/cast/transport/cast_transport_sender.h" | 25 #include "media/cast/transport/cast_transport_sender.h" |
| 23 #include "media/cast/transport/transport/udp_transport.h" | 26 #include "media/cast/transport/transport/udp_transport.h" |
| 24 #include "ui/gfx/size.h" | 27 #include "ui/gfx/size.h" |
| 25 | 28 |
| 29 #if defined(USE_SYSTEM_PROTOBUF) | |
| 30 #include <google/protobuf/io/coded_stream.h> | |
| 31 #include <google/protobuf/io/zero_copy_stream_impl_lite.h> | |
| 32 #else | |
| 33 #include "third_party/protobuf/src/google/protobuf/io/coded_stream.h" | |
| 34 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite .h" | |
| 35 #endif | |
| 36 | |
| 26 namespace media { | 37 namespace media { |
| 27 namespace cast { | 38 namespace cast { |
| 28 // Settings chosen to match default receiver settings. | 39 // Settings chosen to match default receiver settings. |
| 29 #define DEFAULT_SEND_PORT "2344" | 40 #define DEFAULT_SEND_PORT "2344" |
| 30 #define DEFAULT_RECEIVE_PORT "2346" | 41 #define DEFAULT_RECEIVE_PORT "2346" |
| 31 #define DEFAULT_SEND_IP "127.0.0.1" | 42 #define DEFAULT_SEND_IP "127.0.0.1" |
| 32 #define DEFAULT_READ_FROM_FILE "0" | 43 #define DEFAULT_READ_FROM_FILE "0" |
| 33 #define DEFAULT_AUDIO_SENDER_SSRC "1" | 44 #define DEFAULT_AUDIO_SENDER_SSRC "1" |
| 34 #define DEFAULT_AUDIO_RECEIVER_SSRC "2" | 45 #define DEFAULT_AUDIO_RECEIVER_SSRC "2" |
| 35 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" | 46 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" |
| 36 #define DEFAULT_VIDEO_SENDER_SSRC "11" | 47 #define DEFAULT_VIDEO_SENDER_SSRC "11" |
| 37 #define DEFAULT_VIDEO_RECEIVER_SSRC "12" | 48 #define DEFAULT_VIDEO_RECEIVER_SSRC "12" |
| 38 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" | 49 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" |
| 39 #define DEFAULT_VIDEO_CODEC_WIDTH "1280" | 50 #define DEFAULT_VIDEO_CODEC_WIDTH "1280" |
| 40 #define DEFAULT_VIDEO_CODEC_HEIGHT "720" | 51 #define DEFAULT_VIDEO_CODEC_HEIGHT "720" |
| 41 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" | 52 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" |
| 42 #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" | 53 #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" |
| 43 #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" | 54 #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" |
| 44 | 55 |
| 56 #define DEFAULT_LOGGING_DURATION "300" | |
| 57 | |
| 45 namespace { | 58 namespace { |
| 46 static const int kAudioChannels = 2; | 59 static const int kAudioChannels = 2; |
| 47 static const int kAudioSamplingFrequency = 48000; | 60 static const int kAudioSamplingFrequency = 48000; |
| 48 static const int kSoundFrequency = 1234; // Frequency of sinusoid wave. | 61 static const int kSoundFrequency = 1234; // Frequency of sinusoid wave. |
| 49 // The tests are commonly implemented with |kFrameTimerMs| RunTask function; | 62 // The tests are commonly implemented with |kFrameTimerMs| RunTask function; |
| 50 // a normal video is 30 fps hence the 33 ms between frames. | 63 // a normal video is 30 fps hence the 33 ms between frames. |
| 51 static const float kSoundVolume = 0.5f; | 64 static const float kSoundVolume = 0.5f; |
| 52 static const int kFrameTimerMs = 33; | 65 static const int kFrameTimerMs = 33; |
| 53 | 66 |
| 54 // Dummy callback function that does nothing except to accept ownership of | 67 // Dummy callback function that does nothing except to accept ownership of |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 70 std::string GetIpAddress(const std::string display_text) { | 83 std::string GetIpAddress(const std::string display_text) { |
| 71 test::InputBuilder input(display_text, DEFAULT_SEND_IP, INT_MIN, INT_MAX); | 84 test::InputBuilder input(display_text, DEFAULT_SEND_IP, INT_MIN, INT_MAX); |
| 72 std::string ip_address = input.GetStringInput(); | 85 std::string ip_address = input.GetStringInput(); |
| 73 // Verify correct form: | 86 // Verify correct form: |
| 74 while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) { | 87 while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) { |
| 75 ip_address = input.GetStringInput(); | 88 ip_address = input.GetStringInput(); |
| 76 } | 89 } |
| 77 return ip_address; | 90 return ip_address; |
| 78 } | 91 } |
| 79 | 92 |
| 93 int GetLoggingDuration() { | |
| 94 test::InputBuilder input( | |
| 95 "Choose logging duration (seconds), 0 for no logging.", | |
| 96 DEFAULT_LOGGING_DURATION, 0, INT_MAX); | |
| 97 return input.GetIntInput(); | |
| 98 } | |
| 99 | |
| 100 std::string GetLogFileDestination() { | |
| 101 test::InputBuilder input( | |
| 102 "Enter log file destination.", | |
| 103 "/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.
| |
| 104 return input.GetStringInput(); | |
| 105 } | |
| 106 | |
| 80 bool ReadFromFile() { | 107 bool ReadFromFile() { |
| 81 test::InputBuilder input( | 108 test::InputBuilder input( |
| 82 "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); | 109 "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); |
| 83 return (1 == input.GetIntInput()); | 110 return (1 == input.GetIntInput()); |
| 84 } | 111 } |
| 85 | 112 |
| 86 std::string GetVideoFile() { | 113 std::string GetVideoFile() { |
| 87 test::InputBuilder input( | 114 test::InputBuilder input( |
| 88 "Enter file and path to raw video file.", "", INT_MIN, INT_MAX); | 115 "Enter file and path to raw video file.", "", INT_MIN, INT_MAX); |
| 89 return input.GetStringInput(); | 116 return input.GetStringInput(); |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 CHECK_EQ(result, media::cast::STATUS_INITIALIZED); | 332 CHECK_EQ(result, media::cast::STATUS_INITIALIZED); |
| 306 VLOG(1) << "Cast Sender initialized"; | 333 VLOG(1) << "Cast Sender initialized"; |
| 307 } | 334 } |
| 308 | 335 |
| 309 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { | 336 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { |
| 310 net::IPAddressNumber ip_number; | 337 net::IPAddressNumber ip_number; |
| 311 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number)); | 338 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number)); |
| 312 return net::IPEndPoint(ip_number, port); | 339 return net::IPEndPoint(ip_number, port); |
| 313 } | 340 } |
| 314 | 341 |
| 342 // 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
| |
| 343 void WriteTo(const media::cast::FrameEventMap& frame_events, | |
| 344 const media::cast::PacketEventMap& packet_events, | |
| 345 const media::cast::GenericEventMap& generic_events, | |
| 346 std::string* output_string) { | |
| 347 google::protobuf::io::StringOutputStream string_output_stream(output_string); | |
| 348 | |
| 349 google::protobuf::io::CodedOutputStream output_stream(&string_output_stream); | |
| 350 | |
| 351 int byte_count = 0; | |
| 352 int last_byte_count = 0; | |
| 353 // 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.
| |
| 354 output_stream.WriteLittleEndian32(frame_events.size()); | |
| 355 for (media::cast::FrameEventMap::const_iterator it = frame_events.begin(); | |
| 356 it != frame_events.end(); ++it) { | |
| 357 int proto_size = it->second->ByteSize(); | |
| 358 | |
| 359 // 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.
| |
| 360 output_stream.WriteLittleEndian32(proto_size); | |
| 361 | |
| 362 bool success = it->second->SerializeToCodedStream(&output_stream); | |
| 363 DCHECK(success); | |
| 364 } | |
| 365 | |
| 366 byte_count = output_stream.ByteCount(); | |
| 367 printf("Encoded frame events byte count: %d\n", byte_count - last_byte_count); | |
| 368 last_byte_count = byte_count; | |
| 369 | |
| 370 // 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.
| |
| 371 output_stream.WriteLittleEndian32(packet_events.size()); | |
| 372 for (media::cast::PacketEventMap::const_iterator it = packet_events.begin(); | |
| 373 it != packet_events.end(); ++it) { | |
| 374 int proto_size = it->second->ByteSize(); | |
| 375 // 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.
| |
| 376 output_stream.WriteLittleEndian32(proto_size); | |
| 377 | |
| 378 bool success = it->second->SerializeToCodedStream(&output_stream); | |
| 379 DCHECK(success); | |
| 380 } | |
| 381 | |
| 382 byte_count = output_stream.ByteCount(); | |
| 383 printf("Encoded packet events byte count: %d\n", | |
| 384 byte_count - last_byte_count); | |
| 385 last_byte_count = byte_count; | |
| 386 | |
| 387 // 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.
| |
| 388 output_stream.WriteLittleEndian32(generic_events.size()); | |
| 389 for (media::cast::GenericEventMap::const_iterator it = generic_events.begin(); | |
| 390 it != generic_events.end(); ++it) { | |
| 391 // 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.
| |
| 392 output_stream.WriteLittleEndian32(it->second->ByteSize()); | |
| 393 | |
| 394 bool success = it->second->SerializeToCodedStream(&output_stream); | |
| 395 DCHECK(success); | |
| 396 } | |
| 397 | |
| 398 byte_count = output_stream.ByteCount(); | |
| 399 printf("Encoded generic events byte count: %d\n", | |
| 400 byte_count - last_byte_count); | |
| 401 last_byte_count = byte_count; | |
| 402 } | |
| 403 | |
| 404 void WriteLogsToFileAndStopSubscribing( | |
| 405 const scoped_refptr<media::cast::CastEnvironment>& cast_environment, | |
| 406 scoped_ptr<media::cast::EncodingEventSubscriber> subscriber, | |
| 407 file_util::ScopedFILE log_file) { | |
| 408 cast_environment->Logging()->RemoveRawEventSubscriber(subscriber.get()); | |
| 409 media::cast::FrameEventMap frame_events; | |
| 410 media::cast::PacketEventMap packet_events; | |
| 411 media::cast::GenericEventMap generic_events; | |
| 412 subscriber->GetFrameEventsAndReset(&frame_events); | |
| 413 subscriber->GetPacketEventsAndReset(&packet_events); | |
| 414 subscriber->GetGenericEventsAndReset(&generic_events); | |
| 415 | |
| 416 // 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.
| |
| 417 // so that it won't block the main thread. | |
| 418 printf("Frame map size: %lu\n", frame_events.size()); | |
| 419 printf("Packet map size: %lu\n", packet_events.size()); | |
| 420 printf("Generic map size: %lu\n", generic_events.size()); | |
| 421 | |
| 422 std::string serialized_string; | |
| 423 WriteTo(frame_events, packet_events, generic_events, &serialized_string); | |
| 424 | |
| 425 size_t ret = fwrite(&serialized_string[0], 1, serialized_string.size(), | |
| 426 log_file.get()); | |
| 427 if (ret != serialized_string.size()) | |
| 428 VLOG(1) << "Failed to write logs to file."; | |
| 429 } | |
| 430 | |
| 315 } // namespace | 431 } // namespace |
| 316 | 432 |
| 317 int main(int argc, char** argv) { | 433 int main(int argc, char** argv) { |
| 318 base::AtExitManager at_exit; | 434 base::AtExitManager at_exit; |
| 319 VLOG(1) << "Cast Sender"; | 435 VLOG(1) << "Cast Sender"; |
| 320 base::Thread test_thread("Cast sender test app thread"); | 436 base::Thread test_thread("Cast sender test app thread"); |
| 321 base::Thread audio_thread("Cast audio encoder thread"); | 437 base::Thread audio_thread("Cast audio encoder thread"); |
| 322 base::Thread video_thread("Cast video encoder thread"); | 438 base::Thread video_thread("Cast video encoder thread"); |
| 323 test_thread.Start(); | 439 test_thread.Start(); |
| 324 audio_thread.Start(); | 440 audio_thread.Start(); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 348 config.audio_rtp_config = audio_config.rtp_config; | 464 config.audio_rtp_config = audio_config.rtp_config; |
| 349 config.video_rtp_config = video_config.rtp_config; | 465 config.video_rtp_config = video_config.rtp_config; |
| 350 | 466 |
| 351 scoped_ptr<media::cast::transport::CastTransportSender> transport_sender( | 467 scoped_ptr<media::cast::transport::CastTransportSender> transport_sender( |
| 352 media::cast::transport::CastTransportSender::CreateCastTransportSender( | 468 media::cast::transport::CastTransportSender::CreateCastTransportSender( |
| 353 clock.get(), | 469 clock.get(), |
| 354 config, | 470 config, |
| 355 base::Bind(&UpdateCastTransportStatus), | 471 base::Bind(&UpdateCastTransportStatus), |
| 356 io_message_loop.message_loop_proxy())); | 472 io_message_loop.message_loop_proxy())); |
| 357 | 473 |
| 358 // Enable main and send side threads only. Disable logging. | 474 // Enable main and send side threads only. Enable raw event logging. |
| 359 // Running transport on the main thread. | 475 // Running transport on the main thread. |
| 476 media::cast::CastLoggingConfig logging_config(true); | |
| 477 logging_config.enable_raw_data_collection = true; | |
| 478 | |
| 360 scoped_refptr<media::cast::CastEnvironment> cast_environment( | 479 scoped_refptr<media::cast::CastEnvironment> cast_environment( |
| 361 new media::cast::CastEnvironment( | 480 new media::cast::CastEnvironment( |
| 362 clock.Pass(), | 481 clock.Pass(), |
| 363 io_message_loop.message_loop_proxy(), | 482 io_message_loop.message_loop_proxy(), |
| 364 audio_thread.message_loop_proxy(), | 483 audio_thread.message_loop_proxy(), |
| 365 NULL, | 484 NULL, |
| 366 video_thread.message_loop_proxy(), | 485 video_thread.message_loop_proxy(), |
| 367 NULL, | 486 NULL, |
| 368 io_message_loop.message_loop_proxy(), | 487 io_message_loop.message_loop_proxy(), |
| 369 media::cast::GetDefaultCastSenderLoggingConfig())); | 488 logging_config)); |
| 370 | 489 |
| 371 scoped_ptr<media::cast::CastSender> cast_sender( | 490 scoped_ptr<media::cast::CastSender> cast_sender( |
| 372 media::cast::CastSender::CreateCastSender( | 491 media::cast::CastSender::CreateCastSender( |
| 373 cast_environment, | 492 cast_environment, |
| 374 audio_config, | 493 audio_config, |
| 375 video_config, | 494 video_config, |
| 376 NULL, // gpu_factories. | 495 NULL, // gpu_factories. |
| 377 base::Bind(&InitializationResult), | 496 base::Bind(&InitializationResult), |
| 378 transport_sender.get())); | 497 transport_sender.get())); |
| 379 | 498 |
| 380 transport_sender->SetPacketReceiver(cast_sender->packet_receiver()); | 499 transport_sender->SetPacketReceiver(cast_sender->packet_receiver()); |
| 381 | 500 |
| 382 media::cast::FrameInput* frame_input = cast_sender->frame_input(); | 501 media::cast::FrameInput* frame_input = cast_sender->frame_input(); |
| 383 scoped_ptr<media::cast::SendProcess> send_process( | 502 scoped_ptr<media::cast::SendProcess> send_process( |
| 384 new media::cast::SendProcess(test_thread.message_loop_proxy(), | 503 new media::cast::SendProcess(test_thread.message_loop_proxy(), |
| 385 cast_environment->Clock(), | 504 cast_environment->Clock(), |
| 386 video_config, | 505 video_config, |
| 387 frame_input)); | 506 frame_input)); |
| 388 | 507 |
| 508 // Set up event subscribers. | |
| 509 // TODO(imcheng): Set up separate subscribers for audio / video / other. | |
| 510 int logging_duration = media::cast::GetLoggingDuration(); | |
| 511 scoped_ptr<media::cast::EncodingEventSubscriber> event_subscriber; | |
| 512 if (logging_duration > 0) { | |
| 513 std::string log_file_name(media::cast::GetLogFileDestination()); | |
| 514 event_subscriber.reset(new media::cast::EncodingEventSubscriber); | |
| 515 cast_environment->Logging()->AddRawEventSubscriber(event_subscriber.get()); | |
| 516 file_util::ScopedFILE log_file(fopen(log_file_name.c_str(), "w")); | |
| 517 if (!log_file) { | |
| 518 printf("Failed to open log file for writing.\n"); | |
| 519 exit(-1); | |
| 520 } | |
| 521 | |
| 522 io_message_loop.message_loop_proxy()->PostDelayedTask(FROM_HERE, | |
| 523 base::Bind(&WriteLogsToFileAndStopSubscribing, | |
| 524 cast_environment, | |
| 525 base::Passed(&event_subscriber), | |
| 526 base::Passed(&log_file)), | |
| 527 base::TimeDelta::FromSeconds(logging_duration)); | |
| 528 } | |
| 529 | |
| 389 test_thread.message_loop_proxy()->PostTask( | 530 test_thread.message_loop_proxy()->PostTask( |
| 390 FROM_HERE, | 531 FROM_HERE, |
| 391 base::Bind(&media::cast::SendProcess::SendFrame, | 532 base::Bind(&media::cast::SendProcess::SendFrame, |
| 392 base::Unretained(send_process.get()))); | 533 base::Unretained(send_process.get()))); |
| 393 | 534 |
| 394 io_message_loop.Run(); | 535 io_message_loop.Run(); |
| 536 | |
| 395 return 0; | 537 return 0; |
| 396 } | 538 } |
| OLD | NEW |