| 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" | |
| 10 #include "base/logging.h" | 9 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/threading/thread.h" | 11 #include "base/threading/thread.h" |
| 13 #include "base/time/default_tick_clock.h" | 12 #include "base/time/default_tick_clock.h" |
| 14 #include "media/base/video_frame.h" | 13 #include "media/base/video_frame.h" |
| 15 #include "media/cast/cast_config.h" | 14 #include "media/cast/cast_config.h" |
| 16 #include "media/cast/cast_environment.h" | 15 #include "media/cast/cast_environment.h" |
| 17 #include "media/cast/cast_sender.h" | 16 #include "media/cast/cast_sender.h" |
| 18 #include "media/cast/logging/encoding_event_subscriber.h" | |
| 19 #include "media/cast/logging/logging_defines.h" | 17 #include "media/cast/logging/logging_defines.h" |
| 20 #include "media/cast/logging/proto/raw_events.pb.h" | |
| 21 #include "media/cast/test/utility/audio_utility.h" | 18 #include "media/cast/test/utility/audio_utility.h" |
| 22 #include "media/cast/test/utility/input_builder.h" | 19 #include "media/cast/test/utility/input_builder.h" |
| 23 #include "media/cast/test/utility/video_utility.h" | 20 #include "media/cast/test/utility/video_utility.h" |
| 24 #include "media/cast/transport/cast_transport_defines.h" | 21 #include "media/cast/transport/cast_transport_defines.h" |
| 25 #include "media/cast/transport/cast_transport_sender.h" | 22 #include "media/cast/transport/cast_transport_sender.h" |
| 26 #include "media/cast/transport/transport/udp_transport.h" | 23 #include "media/cast/transport/transport/udp_transport.h" |
| 27 #include "net/base/big_endian.h" | |
| 28 #include "ui/gfx/size.h" | 24 #include "ui/gfx/size.h" |
| 29 | 25 |
| 30 namespace media { | 26 namespace media { |
| 31 namespace cast { | 27 namespace cast { |
| 32 // Settings chosen to match default receiver settings. | 28 // Settings chosen to match default receiver settings. |
| 33 #define DEFAULT_RECEIVER_PORT "2344" | 29 #define DEFAULT_RECEIVER_PORT "2344" |
| 34 #define DEFAULT_RECEIVER_IP "127.0.0.1" | 30 #define DEFAULT_RECEIVER_IP "127.0.0.1" |
| 35 #define DEFAULT_READ_FROM_FILE "0" | 31 #define DEFAULT_READ_FROM_FILE "0" |
| 36 #define DEFAULT_AUDIO_SENDER_SSRC "1" | 32 #define DEFAULT_AUDIO_SENDER_SSRC "1" |
| 37 #define DEFAULT_AUDIO_RECEIVER_SSRC "2" | 33 #define DEFAULT_AUDIO_RECEIVER_SSRC "2" |
| 38 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" | 34 #define DEFAULT_AUDIO_PAYLOAD_TYPE "127" |
| 39 #define DEFAULT_VIDEO_SENDER_SSRC "11" | 35 #define DEFAULT_VIDEO_SENDER_SSRC "11" |
| 40 #define DEFAULT_VIDEO_RECEIVER_SSRC "12" | 36 #define DEFAULT_VIDEO_RECEIVER_SSRC "12" |
| 41 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" | 37 #define DEFAULT_VIDEO_PAYLOAD_TYPE "96" |
| 42 #define DEFAULT_VIDEO_CODEC_WIDTH "1280" | 38 #define DEFAULT_VIDEO_CODEC_WIDTH "1280" |
| 43 #define DEFAULT_VIDEO_CODEC_HEIGHT "720" | 39 #define DEFAULT_VIDEO_CODEC_HEIGHT "720" |
| 44 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" | 40 #define DEFAULT_VIDEO_CODEC_BITRATE "2000" |
| 45 #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" | 41 #define DEFAULT_VIDEO_CODEC_MAX_BITRATE "4000" |
| 46 #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" | 42 #define DEFAULT_VIDEO_CODEC_MIN_BITRATE "1000" |
| 47 | 43 |
| 48 #define DEFAULT_LOGGING_DURATION "30" | |
| 49 | |
| 50 namespace { | 44 namespace { |
| 51 static const int kAudioChannels = 2; | 45 static const int kAudioChannels = 2; |
| 52 static const int kAudioSamplingFrequency = 48000; | 46 static const int kAudioSamplingFrequency = 48000; |
| 53 static const int kSoundFrequency = 1234; // Frequency of sinusoid wave. | 47 static const int kSoundFrequency = 1234; // Frequency of sinusoid wave. |
| 54 // The tests are commonly implemented with |kFrameTimerMs| RunTask function; | 48 // The tests are commonly implemented with |kFrameTimerMs| RunTask function; |
| 55 // a normal video is 30 fps hence the 33 ms between frames. | 49 // a normal video is 30 fps hence the 33 ms between frames. |
| 56 static const float kSoundVolume = 0.5f; | 50 static const float kSoundVolume = 0.5f; |
| 57 static const int kFrameTimerMs = 33; | 51 static const int kFrameTimerMs = 33; |
| 58 | 52 |
| 59 // Dummy callback function that does nothing except to accept ownership of | 53 // Dummy callback function that does nothing except to accept ownership of |
| (...skipping 11 matching lines...) Expand all Loading... |
| 71 std::string GetIpAddress(const std::string display_text) { | 65 std::string GetIpAddress(const std::string display_text) { |
| 72 test::InputBuilder input(display_text, DEFAULT_RECEIVER_IP, INT_MIN, INT_MAX); | 66 test::InputBuilder input(display_text, DEFAULT_RECEIVER_IP, INT_MIN, INT_MAX); |
| 73 std::string ip_address = input.GetStringInput(); | 67 std::string ip_address = input.GetStringInput(); |
| 74 // Verify correct form: | 68 // Verify correct form: |
| 75 while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) { | 69 while (std::count(ip_address.begin(), ip_address.end(), '.') != 3) { |
| 76 ip_address = input.GetStringInput(); | 70 ip_address = input.GetStringInput(); |
| 77 } | 71 } |
| 78 return ip_address; | 72 return ip_address; |
| 79 } | 73 } |
| 80 | 74 |
| 81 int GetLoggingDuration() { | |
| 82 test::InputBuilder input( | |
| 83 "Choose logging duration (seconds), 0 for no logging.", | |
| 84 DEFAULT_LOGGING_DURATION, 0, INT_MAX); | |
| 85 return input.GetIntInput(); | |
| 86 } | |
| 87 | |
| 88 std::string GetLogFileDestination() { | |
| 89 test::InputBuilder input( | |
| 90 "Enter log file destination.", | |
| 91 "./raw_events.log", INT_MIN, INT_MAX); | |
| 92 return input.GetStringInput(); | |
| 93 } | |
| 94 | |
| 95 bool ReadFromFile() { | 75 bool ReadFromFile() { |
| 96 test::InputBuilder input( | 76 test::InputBuilder input( |
| 97 "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); | 77 "Enter 1 to read from file.", DEFAULT_READ_FROM_FILE, 0, 1); |
| 98 return (1 == input.GetIntInput()); | 78 return (1 == input.GetIntInput()); |
| 99 } | 79 } |
| 100 | 80 |
| 101 std::string GetVideoFile() { | 81 std::string GetVideoFile() { |
| 102 test::InputBuilder input( | 82 test::InputBuilder input( |
| 103 "Enter file and path to raw video file.", "", INT_MIN, INT_MAX); | 83 "Enter file and path to raw video file.", "", INT_MIN, INT_MAX); |
| 104 return input.GetStringInput(); | 84 return input.GetStringInput(); |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 320 CHECK_EQ(result, media::cast::STATUS_INITIALIZED); | 300 CHECK_EQ(result, media::cast::STATUS_INITIALIZED); |
| 321 VLOG(1) << "Cast Sender initialized"; | 301 VLOG(1) << "Cast Sender initialized"; |
| 322 } | 302 } |
| 323 | 303 |
| 324 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { | 304 net::IPEndPoint CreateUDPAddress(std::string ip_str, int port) { |
| 325 net::IPAddressNumber ip_number; | 305 net::IPAddressNumber ip_number; |
| 326 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number)); | 306 CHECK(net::ParseIPLiteralToNumber(ip_str, &ip_number)); |
| 327 return net::IPEndPoint(ip_number, port); | 307 return net::IPEndPoint(ip_number, port); |
| 328 } | 308 } |
| 329 | 309 |
| 330 // TODO(imcheng): Extract this function to another file. | |
| 331 bool WriteTo(const media::cast::FrameEventMap& frame_events, | |
| 332 const media::cast::PacketEventMap& packet_events, | |
| 333 const int stream_id, | |
| 334 std::string* output) { | |
| 335 // Allow 20MB for encoded uncompressed logs. | |
| 336 const int kMaxEncodedSize = 20 * 1000 * 1000; | |
| 337 output->resize(kMaxEncodedSize); | |
| 338 | |
| 339 net::BigEndianWriter writer(&(*output)[0], output->size()); | |
| 340 | |
| 341 // Write stream id first. | |
| 342 bool success = writer.WriteU32(stream_id); | |
| 343 if (!success) | |
| 344 return false; | |
| 345 | |
| 346 // Frame events - write size first, then write entries | |
| 347 int frame_events_size = frame_events.size(); | |
| 348 success = writer.WriteU32(frame_events_size); | |
| 349 if (!success) | |
| 350 return false; | |
| 351 | |
| 352 for (media::cast::FrameEventMap::const_iterator it = frame_events.begin(); | |
| 353 it != frame_events.end(); ++it) { | |
| 354 int proto_size = it->second->ByteSize(); | |
| 355 | |
| 356 // Write size of the proto, then write the proto | |
| 357 success = writer.WriteU16(proto_size); | |
| 358 if (!success) | |
| 359 return false; | |
| 360 | |
| 361 success = it->second->SerializeToArray(writer.ptr(), writer.remaining()); | |
| 362 if (!success) | |
| 363 return false; | |
| 364 success = writer.Skip(proto_size); | |
| 365 if (!success) | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 // Write packet events | |
| 370 int packet_event_size = packet_events.size(); | |
| 371 success = writer.WriteU32(packet_event_size); | |
| 372 if (!success) | |
| 373 return false; | |
| 374 for (media::cast::PacketEventMap::const_iterator it = packet_events.begin(); | |
| 375 it != packet_events.end(); ++it) { | |
| 376 int proto_size = it->second->ByteSize(); | |
| 377 // Write size of the proto, then write the proto | |
| 378 success = writer.WriteU16(proto_size); | |
| 379 if (!success) | |
| 380 return false; | |
| 381 success = it->second->SerializeToArray(writer.ptr(), writer.remaining()); | |
| 382 if (!success) | |
| 383 return false; | |
| 384 success = writer.Skip(proto_size); | |
| 385 if (!success) | |
| 386 return false; | |
| 387 } | |
| 388 | |
| 389 output->resize(output->size() - writer.remaining()); | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 void WriteLogsToFileAndStopSubscribing( | |
| 394 const scoped_refptr<media::cast::CastEnvironment>& cast_environment, | |
| 395 scoped_ptr<media::cast::EncodingEventSubscriber> video_subscriber, | |
| 396 scoped_ptr<media::cast::EncodingEventSubscriber> audio_subscriber, | |
| 397 file_util::ScopedFILE log_file) { | |
| 398 cast_environment->Logging()->RemoveRawEventSubscriber(video_subscriber.get()); | |
| 399 cast_environment->Logging()->RemoveRawEventSubscriber(audio_subscriber.get()); | |
| 400 media::cast::FrameEventMap frame_events; | |
| 401 media::cast::PacketEventMap packet_events; | |
| 402 video_subscriber->GetFrameEventsAndReset(&frame_events); | |
| 403 video_subscriber->GetPacketEventsAndReset(&packet_events); | |
| 404 | |
| 405 printf("Video frame map size: %lu\n", frame_events.size()); | |
| 406 printf("Video packet map size: %lu\n", packet_events.size()); | |
| 407 | |
| 408 std::string serialized_video_log; | |
| 409 bool success = WriteTo(frame_events, packet_events, 0, &serialized_video_log); | |
| 410 if (!success) { | |
| 411 VLOG(1) << "Failed to serialize video events."; | |
| 412 return; | |
| 413 } | |
| 414 | |
| 415 printf("Serialized video log size: %lu\n", serialized_video_log.size()); | |
| 416 | |
| 417 audio_subscriber->GetFrameEventsAndReset(&frame_events); | |
| 418 audio_subscriber->GetPacketEventsAndReset(&packet_events); | |
| 419 | |
| 420 printf("Audio frame map size: %lu\n", frame_events.size()); | |
| 421 printf("Audio packet map size: %lu\n", packet_events.size()); | |
| 422 | |
| 423 std::string serialized_audio_log; | |
| 424 success = WriteTo(frame_events, packet_events, 1, &serialized_audio_log); | |
| 425 if (!success) { | |
| 426 VLOG(1) << "Failed to serialize audio events."; | |
| 427 return; | |
| 428 } | |
| 429 | |
| 430 printf("Serialized audio log size: %lu\n", serialized_audio_log.size()); | |
| 431 | |
| 432 size_t ret = fwrite(&serialized_video_log[0], 1, serialized_video_log.size(), | |
| 433 log_file.get()); | |
| 434 if (ret != serialized_video_log.size()) { | |
| 435 VLOG(1) << "Failed to write logs to file."; | |
| 436 return; | |
| 437 } | |
| 438 ret = fwrite(&serialized_audio_log[0], 1, serialized_audio_log.size(), | |
| 439 log_file.get()); | |
| 440 if (ret != serialized_audio_log.size()) { | |
| 441 VLOG(1) << "Failed to write logs to file."; | |
| 442 return; | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 } // namespace | 310 } // namespace |
| 447 | 311 |
| 448 int main(int argc, char** argv) { | 312 int main(int argc, char** argv) { |
| 449 base::AtExitManager at_exit; | 313 base::AtExitManager at_exit; |
| 450 VLOG(1) << "Cast Sender"; | 314 VLOG(1) << "Cast Sender"; |
| 451 base::Thread test_thread("Cast sender test app thread"); | 315 base::Thread test_thread("Cast sender test app thread"); |
| 452 base::Thread audio_thread("Cast audio encoder thread"); | 316 base::Thread audio_thread("Cast audio encoder thread"); |
| 453 base::Thread video_thread("Cast video encoder thread"); | 317 base::Thread video_thread("Cast video encoder thread"); |
| 454 test_thread.Start(); | 318 test_thread.Start(); |
| 455 audio_thread.Start(); | 319 audio_thread.Start(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 478 config.audio_rtp_config = audio_config.rtp_config; | 342 config.audio_rtp_config = audio_config.rtp_config; |
| 479 config.video_rtp_config = video_config.rtp_config; | 343 config.video_rtp_config = video_config.rtp_config; |
| 480 | 344 |
| 481 scoped_ptr<media::cast::transport::CastTransportSender> transport_sender( | 345 scoped_ptr<media::cast::transport::CastTransportSender> transport_sender( |
| 482 media::cast::transport::CastTransportSender::CreateCastTransportSender( | 346 media::cast::transport::CastTransportSender::CreateCastTransportSender( |
| 483 clock.get(), | 347 clock.get(), |
| 484 config, | 348 config, |
| 485 base::Bind(&UpdateCastTransportStatus), | 349 base::Bind(&UpdateCastTransportStatus), |
| 486 io_message_loop.message_loop_proxy())); | 350 io_message_loop.message_loop_proxy())); |
| 487 | 351 |
| 488 // Enable main and send side threads only. Enable raw event logging. | 352 // Enable main and send side threads only. Disable logging. |
| 489 // Running transport on the main thread. | 353 // Running transport on the main thread. |
| 490 media::cast::CastLoggingConfig logging_config; | |
| 491 logging_config.enable_raw_data_collection = true; | |
| 492 | |
| 493 scoped_refptr<media::cast::CastEnvironment> cast_environment( | 354 scoped_refptr<media::cast::CastEnvironment> cast_environment( |
| 494 new media::cast::CastEnvironment( | 355 new media::cast::CastEnvironment( |
| 495 clock.Pass(), | 356 clock.Pass(), |
| 496 io_message_loop.message_loop_proxy(), | 357 io_message_loop.message_loop_proxy(), |
| 497 audio_thread.message_loop_proxy(), | 358 audio_thread.message_loop_proxy(), |
| 498 NULL, | 359 NULL, |
| 499 video_thread.message_loop_proxy(), | 360 video_thread.message_loop_proxy(), |
| 500 NULL, | 361 NULL, |
| 501 io_message_loop.message_loop_proxy(), | 362 io_message_loop.message_loop_proxy(), |
| 502 logging_config)); | 363 media::cast::GetDefaultCastSenderLoggingConfig())); |
| 503 | 364 |
| 504 scoped_ptr<media::cast::CastSender> cast_sender( | 365 scoped_ptr<media::cast::CastSender> cast_sender( |
| 505 media::cast::CastSender::CreateCastSender( | 366 media::cast::CastSender::CreateCastSender( |
| 506 cast_environment, | 367 cast_environment, |
| 507 &audio_config, | 368 &audio_config, |
| 508 &video_config, | 369 &video_config, |
| 509 NULL, // gpu_factories. | 370 NULL, // gpu_factories. |
| 510 base::Bind(&InitializationResult), | 371 base::Bind(&InitializationResult), |
| 511 transport_sender.get())); | 372 transport_sender.get())); |
| 512 | 373 |
| 513 transport_sender->SetPacketReceiver(cast_sender->packet_receiver()); | 374 transport_sender->SetPacketReceiver(cast_sender->packet_receiver()); |
| 514 | 375 |
| 515 media::cast::FrameInput* frame_input = cast_sender->frame_input(); | 376 media::cast::FrameInput* frame_input = cast_sender->frame_input(); |
| 516 scoped_ptr<media::cast::SendProcess> send_process( | 377 scoped_ptr<media::cast::SendProcess> send_process( |
| 517 new media::cast::SendProcess(test_thread.message_loop_proxy(), | 378 new media::cast::SendProcess(test_thread.message_loop_proxy(), |
| 518 cast_environment->Clock(), | 379 cast_environment->Clock(), |
| 519 video_config, | 380 video_config, |
| 520 frame_input)); | 381 frame_input)); |
| 521 | 382 |
| 522 // Set up event subscribers. | |
| 523 // TODO(imcheng): Set up separate subscribers for audio / video / other. | |
| 524 int logging_duration = media::cast::GetLoggingDuration(); | |
| 525 scoped_ptr<media::cast::EncodingEventSubscriber> video_event_subscriber; | |
| 526 scoped_ptr<media::cast::EncodingEventSubscriber> audio_event_subscriber; | |
| 527 if (logging_duration > 0) { | |
| 528 std::string log_file_name(media::cast::GetLogFileDestination()); | |
| 529 video_event_subscriber.reset(new media::cast::EncodingEventSubscriber( | |
| 530 media::cast::VIDEO_EVENT, 10000)); | |
| 531 audio_event_subscriber.reset(new media::cast::EncodingEventSubscriber( | |
| 532 media::cast::AUDIO_EVENT, 10000)); | |
| 533 cast_environment->Logging()->AddRawEventSubscriber( | |
| 534 video_event_subscriber.get()); | |
| 535 cast_environment->Logging()->AddRawEventSubscriber( | |
| 536 audio_event_subscriber.get()); | |
| 537 file_util::ScopedFILE log_file(fopen(log_file_name.c_str(), "w")); | |
| 538 if (!log_file) { | |
| 539 printf("Failed to open log file for writing.\n"); | |
| 540 exit(-1); | |
| 541 } | |
| 542 | |
| 543 io_message_loop.message_loop_proxy()->PostDelayedTask(FROM_HERE, | |
| 544 base::Bind(&WriteLogsToFileAndStopSubscribing, | |
| 545 cast_environment, | |
| 546 base::Passed(&video_event_subscriber), | |
| 547 base::Passed(&audio_event_subscriber), | |
| 548 base::Passed(&log_file)), | |
| 549 base::TimeDelta::FromSeconds(logging_duration)); | |
| 550 } | |
| 551 | |
| 552 test_thread.message_loop_proxy()->PostTask( | 383 test_thread.message_loop_proxy()->PostTask( |
| 553 FROM_HERE, | 384 FROM_HERE, |
| 554 base::Bind(&media::cast::SendProcess::SendFrame, | 385 base::Bind(&media::cast::SendProcess::SendFrame, |
| 555 base::Unretained(send_process.get()))); | 386 base::Unretained(send_process.get()))); |
| 556 | 387 |
| 557 io_message_loop.Run(); | 388 io_message_loop.Run(); |
| 558 | |
| 559 return 0; | 389 return 0; |
| 560 } | 390 } |
| OLD | NEW |