| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 // Simulate end to end streaming. | 5 // Simulate end to end streaming. |
| 6 // | 6 // |
| 7 // Input: | 7 // Input: |
| 8 // --source= | 8 // --source= |
| 9 // WebM used as the source of video and audio frames. | 9 // WebM used as the source of video and audio frames. |
| 10 // --output= | 10 // --output= |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 #include "media/cast/cast_sender.h" | 39 #include "media/cast/cast_sender.h" |
| 40 #include "media/cast/logging/encoding_event_subscriber.h" | 40 #include "media/cast/logging/encoding_event_subscriber.h" |
| 41 #include "media/cast/logging/log_serializer.h" | 41 #include "media/cast/logging/log_serializer.h" |
| 42 #include "media/cast/logging/logging_defines.h" | 42 #include "media/cast/logging/logging_defines.h" |
| 43 #include "media/cast/logging/proto/raw_events.pb.h" | 43 #include "media/cast/logging/proto/raw_events.pb.h" |
| 44 #include "media/cast/logging/raw_event_subscriber_bundle.h" | 44 #include "media/cast/logging/raw_event_subscriber_bundle.h" |
| 45 #include "media/cast/logging/simple_event_subscriber.h" | 45 #include "media/cast/logging/simple_event_subscriber.h" |
| 46 #include "media/cast/test/fake_media_source.h" | 46 #include "media/cast/test/fake_media_source.h" |
| 47 #include "media/cast/test/fake_single_thread_task_runner.h" | 47 #include "media/cast/test/fake_single_thread_task_runner.h" |
| 48 #include "media/cast/test/loopback_transport.h" | 48 #include "media/cast/test/loopback_transport.h" |
| 49 #include "media/cast/test/proto/network_simulation_model.pb.h" |
| 49 #include "media/cast/test/skewed_tick_clock.h" | 50 #include "media/cast/test/skewed_tick_clock.h" |
| 50 #include "media/cast/test/utility/audio_utility.h" | 51 #include "media/cast/test/utility/audio_utility.h" |
| 51 #include "media/cast/test/utility/default_config.h" | 52 #include "media/cast/test/utility/default_config.h" |
| 52 #include "media/cast/test/utility/test_util.h" | 53 #include "media/cast/test/utility/test_util.h" |
| 53 #include "media/cast/test/utility/udp_proxy.h" | 54 #include "media/cast/test/utility/udp_proxy.h" |
| 54 #include "media/cast/test/utility/video_utility.h" | 55 #include "media/cast/test/utility/video_utility.h" |
| 55 #include "media/cast/transport/cast_transport_config.h" | 56 #include "media/cast/transport/cast_transport_config.h" |
| 56 #include "media/cast/transport/cast_transport_defines.h" | 57 #include "media/cast/transport/cast_transport_defines.h" |
| 57 #include "media/cast/transport/cast_transport_sender.h" | 58 #include "media/cast/transport/cast_transport_sender.h" |
| 58 #include "media/cast/transport/cast_transport_sender_impl.h" | 59 #include "media/cast/transport/cast_transport_sender_impl.h" |
| 59 | 60 |
| 61 using media::cast::proto::IPPModel; |
| 62 using media::cast::proto::NetworkSimulationModel; |
| 63 using media::cast::proto::NetworkSimulationModelType; |
| 64 |
| 60 namespace media { | 65 namespace media { |
| 61 namespace cast { | 66 namespace cast { |
| 62 namespace { | 67 namespace { |
| 63 | |
| 64 const int kTargetDelay = 300; | 68 const int kTargetDelay = 300; |
| 65 const char kSourcePath[] = "source"; | 69 const char kSourcePath[] = "source"; |
| 70 const char kModelPath[] = "model"; |
| 66 const char kOutputPath[] = "output"; | 71 const char kOutputPath[] = "output"; |
| 67 const char kSimulationId[] = "sim-id"; | 72 const char kSimulationId[] = "sim-id"; |
| 73 const char kLibDir[] = "lib-dir"; |
| 68 | 74 |
| 69 void UpdateCastTransportStatus(transport::CastTransportStatus status) { | 75 void UpdateCastTransportStatus(transport::CastTransportStatus status) { |
| 70 LOG(INFO) << "Cast transport status: " << status; | 76 LOG(INFO) << "Cast transport status: " << status; |
| 71 } | 77 } |
| 72 | 78 |
| 73 void AudioInitializationStatus(CastInitializationStatus status) { | 79 void AudioInitializationStatus(CastInitializationStatus status) { |
| 74 LOG(INFO) << "Audio status: " << status; | 80 LOG(INFO) << "Audio status: " << status; |
| 75 } | 81 } |
| 76 | 82 |
| 77 void VideoInitializationStatus(CastInitializationStatus status) { | 83 void VideoInitializationStatus(CastInitializationStatus status) { |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 LOG(ERROR) << "Failed to append to log."; | 157 LOG(ERROR) << "Failed to append to log."; |
| 152 } | 158 } |
| 153 } | 159 } |
| 154 | 160 |
| 155 // Run simulation once. | 161 // Run simulation once. |
| 156 // | 162 // |
| 157 // |output_path| is the path to write serialized log. | 163 // |output_path| is the path to write serialized log. |
| 158 // |extra_data| is extra tagging information to write to log. | 164 // |extra_data| is extra tagging information to write to log. |
| 159 void RunSimulation(const base::FilePath& source_path, | 165 void RunSimulation(const base::FilePath& source_path, |
| 160 const base::FilePath& output_path, | 166 const base::FilePath& output_path, |
| 161 const std::string& extra_data) { | 167 const std::string& extra_data, |
| 168 const NetworkSimulationModel& model) { |
| 162 // Fake clock. Make sure start time is non zero. | 169 // Fake clock. Make sure start time is non zero. |
| 163 base::SimpleTestTickClock testing_clock; | 170 base::SimpleTestTickClock testing_clock; |
| 164 testing_clock.Advance(base::TimeDelta::FromSeconds(1)); | 171 testing_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| 165 | 172 |
| 166 // Task runner. | 173 // Task runner. |
| 167 scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner = | 174 scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner = |
| 168 new test::FakeSingleThreadTaskRunner(&testing_clock); | 175 new test::FakeSingleThreadTaskRunner(&testing_clock); |
| 169 base::ThreadTaskRunnerHandle task_runner_handle(task_runner); | 176 base::ThreadTaskRunnerHandle task_runner_handle(task_runner); |
| 170 | 177 |
| 171 // CastEnvironments. | 178 // CastEnvironments. |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 net::IPEndPoint(), | 243 net::IPEndPoint(), |
| 237 base::Bind(&UpdateCastTransportStatus), | 244 base::Bind(&UpdateCastTransportStatus), |
| 238 base::Bind(&LogTransportEvents, sender_env), | 245 base::Bind(&LogTransportEvents, sender_env), |
| 239 base::TimeDelta::FromSeconds(1), | 246 base::TimeDelta::FromSeconds(1), |
| 240 task_runner, | 247 task_runner, |
| 241 &sender_to_receiver)); | 248 &sender_to_receiver)); |
| 242 scoped_ptr<CastSender> cast_sender( | 249 scoped_ptr<CastSender> cast_sender( |
| 243 CastSender::Create(sender_env, transport_sender.get())); | 250 CastSender::Create(sender_env, transport_sender.get())); |
| 244 | 251 |
| 245 // Build packet pipe. | 252 // Build packet pipe. |
| 246 // TODO(hclam): Allow user to input these parameters. Following | 253 if (model.type() != media::cast::proto::INTERRUPTED_POISSON_PROCESS) { |
| 247 // parameters are taken from a session from real-world data. It is | 254 LOG(ERROR) << "Unknown model type " << model.type() << "."; |
| 248 // chosen here because it gives a difficult environment. | 255 return; |
| 249 std::vector<double> average_rates; | 256 } |
| 250 average_rates.push_back(0.609); | 257 |
| 251 average_rates.push_back(0.495); | 258 const IPPModel& ipp_model = model.ipp(); |
| 252 average_rates.push_back(0.561); | 259 |
| 253 average_rates.push_back(0.458); | 260 std::vector<double> average_rates(ipp_model.average_rate_size()); |
| 254 average_rates.push_back(0.538); | 261 std::copy(ipp_model.average_rate().begin(), ipp_model.average_rate().end(), |
| 255 average_rates.push_back(0.513); | 262 average_rates.begin()); |
| 256 average_rates.push_back(0.585); | 263 test::InterruptedPoissonProcess ipp(average_rates, |
| 257 average_rates.push_back(0.592); | 264 ipp_model.coef_burstiness(), ipp_model.coef_variance(), 0); |
| 258 average_rates.push_back(0.658); | |
| 259 average_rates.push_back(0.556); | |
| 260 average_rates.push_back(0.371); | |
| 261 average_rates.push_back(0.595); | |
| 262 average_rates.push_back(0.490); | |
| 263 average_rates.push_back(0.980); | |
| 264 average_rates.push_back(0.781); | |
| 265 average_rates.push_back(0.463); | |
| 266 test::InterruptedPoissonProcess ipp(average_rates, 0.3, 4.1, 0); | |
| 267 | 265 |
| 268 // Connect sender to receiver. This initializes the pipe. | 266 // Connect sender to receiver. This initializes the pipe. |
| 269 receiver_to_sender.Initialize( | 267 receiver_to_sender.Initialize( |
| 270 ipp.NewBuffer(128 * 1024), cast_sender->packet_receiver(), task_runner, | 268 ipp.NewBuffer(128 * 1024), cast_sender->packet_receiver(), task_runner, |
| 271 &testing_clock); | 269 &testing_clock); |
| 272 sender_to_receiver.Initialize( | 270 sender_to_receiver.Initialize( |
| 273 ipp.NewBuffer(128 * 1024), cast_receiver->packet_receiver(), task_runner, | 271 ipp.NewBuffer(128 * 1024), cast_receiver->packet_receiver(), task_runner, |
| 274 &testing_clock); | 272 &testing_clock); |
| 275 | 273 |
| 276 // Start receiver. | 274 // Start receiver. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 294 CreateDefaultVideoEncodeMemoryCallback()); | 292 CreateDefaultVideoEncodeMemoryCallback()); |
| 295 | 293 |
| 296 // Start sending. | 294 // Start sending. |
| 297 if (!source_path.empty()) { | 295 if (!source_path.empty()) { |
| 298 // 0 means using the FPS from the file. | 296 // 0 means using the FPS from the file. |
| 299 media_source.SetSourceFile(source_path, 0); | 297 media_source.SetSourceFile(source_path, 0); |
| 300 } | 298 } |
| 301 media_source.Start(cast_sender->audio_frame_input(), | 299 media_source.Start(cast_sender->audio_frame_input(), |
| 302 cast_sender->video_frame_input()); | 300 cast_sender->video_frame_input()); |
| 303 | 301 |
| 304 // Run for 5 minutes. | 302 // Run for 3 minutes. |
| 305 base::TimeDelta elapsed_time; | 303 base::TimeDelta elapsed_time; |
| 306 while (elapsed_time.InMinutes() < 5) { | 304 while (elapsed_time.InMinutes() < 3) { |
| 307 // Each step is 100us. | 305 // Each step is 100us. |
| 308 base::TimeDelta step = base::TimeDelta::FromMicroseconds(100); | 306 base::TimeDelta step = base::TimeDelta::FromMicroseconds(100); |
| 309 task_runner->Sleep(step); | 307 task_runner->Sleep(step); |
| 310 elapsed_time += step; | 308 elapsed_time += step; |
| 311 } | 309 } |
| 312 | 310 |
| 313 LOG(INFO) << "Audio frame count: " << audio_frame_count; | 311 LOG(INFO) << "Audio frame count: " << audio_frame_count; |
| 314 LOG(INFO) << "Video frame count: " << video_frame_count; | 312 LOG(INFO) << "Video frame count: " << video_frame_count; |
| 315 LOG(INFO) << "Writing log: " << output_path.value(); | 313 LOG(INFO) << "Writing log: " << output_path.value(); |
| 316 | 314 |
| 317 // Truncate file and then write serialized log. | 315 // Truncate file and then write serialized log. |
| 318 { | 316 { |
| 319 base::ScopedFILE file(base::OpenFile(output_path, "wb")); | 317 base::ScopedFILE file(base::OpenFile(output_path, "wb")); |
| 320 if (!file.get()) { | 318 if (!file.get()) { |
| 321 LOG(INFO) << "Cannot write to log."; | 319 LOG(INFO) << "Cannot write to log."; |
| 322 return; | 320 return; |
| 323 } | 321 } |
| 324 } | 322 } |
| 325 AppendLog(&video_event_subscriber, extra_data, output_path); | 323 AppendLog(&video_event_subscriber, extra_data, output_path); |
| 326 AppendLog(&audio_event_subscriber, extra_data, output_path); | 324 AppendLog(&audio_event_subscriber, extra_data, output_path); |
| 327 } | 325 } |
| 328 | 326 |
| 327 NetworkSimulationModel DefaultModel() { |
| 328 NetworkSimulationModel model; |
| 329 model.set_type(cast::proto::INTERRUPTED_POISSON_PROCESS); |
| 330 IPPModel* ipp = model.mutable_ipp(); |
| 331 ipp->set_coef_burstiness(0.609); |
| 332 ipp->set_coef_variance(4.1); |
| 333 |
| 334 ipp->add_average_rate(0.609); |
| 335 ipp->add_average_rate(0.495); |
| 336 ipp->add_average_rate(0.561); |
| 337 ipp->add_average_rate(0.458); |
| 338 ipp->add_average_rate(0.538); |
| 339 ipp->add_average_rate(0.513); |
| 340 ipp->add_average_rate(0.585); |
| 341 ipp->add_average_rate(0.592); |
| 342 ipp->add_average_rate(0.658); |
| 343 ipp->add_average_rate(0.556); |
| 344 ipp->add_average_rate(0.371); |
| 345 ipp->add_average_rate(0.595); |
| 346 ipp->add_average_rate(0.490); |
| 347 ipp->add_average_rate(0.980); |
| 348 ipp->add_average_rate(0.781); |
| 349 ipp->add_average_rate(0.463); |
| 350 |
| 351 return model; |
| 352 } |
| 353 |
| 354 bool IsModelValid(const NetworkSimulationModel& model) { |
| 355 if (!model.has_type()) |
| 356 return false; |
| 357 NetworkSimulationModelType type = model.type(); |
| 358 if (type == media::cast::proto::INTERRUPTED_POISSON_PROCESS) { |
| 359 if (!model.has_ipp()) |
| 360 return false; |
| 361 const IPPModel& ipp = model.ipp(); |
| 362 if (ipp.coef_burstiness() <= 0.0 || ipp.coef_variance() <= 0.0) |
| 363 return false; |
| 364 if (ipp.average_rate_size() == 0) |
| 365 return false; |
| 366 for (int i = 0; i < ipp.average_rate_size(); i++) { |
| 367 if (ipp.average_rate(i) <= 0.0) |
| 368 return false; |
| 369 } |
| 370 } |
| 371 |
| 372 return true; |
| 373 } |
| 374 |
| 375 NetworkSimulationModel LoadModel(const base::FilePath& model_path) { |
| 376 if (model_path.empty()) { |
| 377 LOG(ERROR) << "Model path not set."; |
| 378 return DefaultModel(); |
| 379 } |
| 380 std::string model_str; |
| 381 if (!base::ReadFileToString(model_path, &model_str)) { |
| 382 LOG(ERROR) << "Failed to read model file."; |
| 383 return DefaultModel(); |
| 384 } |
| 385 |
| 386 NetworkSimulationModel model; |
| 387 if (!model.ParseFromString(model_str)) { |
| 388 LOG(ERROR) << "Failed to parse model."; |
| 389 return DefaultModel(); |
| 390 } |
| 391 if (!IsModelValid(model)) { |
| 392 LOG(ERROR) << "Invalid model."; |
| 393 return DefaultModel(); |
| 394 } |
| 395 |
| 396 return model; |
| 397 } |
| 398 |
| 329 } // namespace | 399 } // namespace |
| 330 } // namespace cast | 400 } // namespace cast |
| 331 } // namespace media | 401 } // namespace media |
| 332 | 402 |
| 333 int main(int argc, char** argv) { | 403 int main(int argc, char** argv) { |
| 334 base::AtExitManager at_exit; | 404 base::AtExitManager at_exit; |
| 335 CommandLine::Init(argc, argv); | 405 CommandLine::Init(argc, argv); |
| 336 InitLogging(logging::LoggingSettings()); | 406 InitLogging(logging::LoggingSettings()); |
| 337 | 407 |
| 338 base::FilePath media_path; | 408 const CommandLine* cmd = CommandLine::ForCurrentProcess(); |
| 339 if (!PathService::Get(base::DIR_MODULE, &media_path)) { | 409 base::FilePath media_path = cmd->GetSwitchValuePath(media::cast::kLibDir); |
| 340 LOG(ERROR) << "Failed to load FFmpeg."; | 410 if (media_path.empty()) { |
| 411 if (!PathService::Get(base::DIR_MODULE, &media_path)) { |
| 412 LOG(ERROR) << "Failed to load FFmpeg."; |
| 413 return 1; |
| 414 } |
| 415 } |
| 416 |
| 417 if (!media::InitializeMediaLibrary(media_path)) { |
| 418 LOG(ERROR) << "Failed to initialize FFmpeg."; |
| 341 return 1; | 419 return 1; |
| 342 } | 420 } |
| 343 media::InitializeMediaLibrary(media_path); | |
| 344 | 421 |
| 345 const CommandLine* cmd = CommandLine::ForCurrentProcess(); | |
| 346 base::FilePath source_path = cmd->GetSwitchValuePath( | 422 base::FilePath source_path = cmd->GetSwitchValuePath( |
| 347 media::cast::kSourcePath); | 423 media::cast::kSourcePath); |
| 348 base::FilePath output_path = cmd->GetSwitchValuePath( | 424 base::FilePath output_path = cmd->GetSwitchValuePath( |
| 349 media::cast::kOutputPath); | 425 media::cast::kOutputPath); |
| 350 if (output_path.empty()) { | 426 if (output_path.empty()) { |
| 351 base::GetTempDir(&output_path); | 427 base::GetTempDir(&output_path); |
| 352 output_path = output_path.AppendASCII("sim-events.gz"); | 428 output_path = output_path.AppendASCII("sim-events.gz"); |
| 353 } | 429 } |
| 354 std::string sim_id = cmd->GetSwitchValueASCII(media::cast::kSimulationId); | 430 std::string sim_id = cmd->GetSwitchValueASCII(media::cast::kSimulationId); |
| 355 | 431 |
| 432 NetworkSimulationModel model = media::cast::LoadModel( |
| 433 cmd->GetSwitchValuePath(media::cast::kModelPath)); |
| 434 |
| 356 base::DictionaryValue values; | 435 base::DictionaryValue values; |
| 357 values.SetBoolean("sim", true); | 436 values.SetBoolean("sim", true); |
| 358 values.SetString("sim-id", sim_id); | 437 values.SetString("sim-id", sim_id); |
| 359 | 438 |
| 360 std::string extra_data; | 439 std::string extra_data; |
| 361 base::JSONWriter::Write(&values, &extra_data); | 440 base::JSONWriter::Write(&values, &extra_data); |
| 362 | 441 |
| 363 // Run. | 442 // Run. |
| 364 media::cast::RunSimulation(source_path, output_path, extra_data); | 443 media::cast::RunSimulation(source_path, output_path, extra_data, model); |
| 365 return 0; | 444 return 0; |
| 366 } | 445 } |
| OLD | NEW |