Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Side by Side Diff: media/cast/test/simulator.cc

Issue 362123005: Cast: Update simulator tool with more inputs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Diff Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 // Simulation program. 5 // Simulate end to end streaming.
6 //
6 // Input: 7 // Input:
7 // - File path to writing out the raw event log of the simulation session. 8 // --source=
8 // - Simulation parameters. 9 // WebM used as the source of video and audio frames.
9 // - Unique simulation run ID for tagging the log. 10 // --output=
11 // File path to writing out the raw event log of the simulation session.
12 // --sim-id=
13 // Unique simulation ID.
14 //
10 // Output: 15 // Output:
11 // - Raw event log of the simulation session tagged with the unique test ID, 16 // - Raw event log of the simulation session tagged with the unique test ID,
12 // written out to the specified file path. 17 // written out to the specified file path.
13 18
14 #include "base/at_exit.h" 19 #include "base/at_exit.h"
20 #include "base/base_paths.h"
15 #include "base/command_line.h" 21 #include "base/command_line.h"
16 #include "base/file_util.h" 22 #include "base/file_util.h"
17 #include "base/files/file_path.h" 23 #include "base/files/file_path.h"
18 #include "base/files/memory_mapped_file.h" 24 #include "base/files/memory_mapped_file.h"
19 #include "base/files/scoped_file.h" 25 #include "base/files/scoped_file.h"
26 #include "base/json/json_writer.h"
20 #include "base/logging.h" 27 #include "base/logging.h"
21 28 #include "base/path_service.h"
22 const char kSimulationRunId[] = "simulation-run-id"; 29 #include "base/test/simple_test_tick_clock.h"
23 const char kOutputPath[] = "output-path"; 30 #include "base/thread_task_runner_handle.h"
31 #include "base/time/tick_clock.h"
32 #include "base/values.h"
33 #include "media/base/audio_bus.h"
34 #include "media/base/media.h"
35 #include "media/base/video_frame.h"
36 #include "media/cast/cast_config.h"
37 #include "media/cast/cast_environment.h"
38 #include "media/cast/cast_receiver.h"
39 #include "media/cast/cast_sender.h"
40 #include "media/cast/logging/encoding_event_subscriber.h"
41 #include "media/cast/logging/log_serializer.h"
42 #include "media/cast/logging/logging_defines.h"
43 #include "media/cast/logging/proto/raw_events.pb.h"
44 #include "media/cast/logging/raw_event_subscriber_bundle.h"
45 #include "media/cast/logging/simple_event_subscriber.h"
46 #include "media/cast/test/fake_media_source.h"
47 #include "media/cast/test/fake_single_thread_task_runner.h"
48 #include "media/cast/test/loopback_transport.h"
49 #include "media/cast/test/proto/network_simulation_model.pb.h"
50 #include "media/cast/test/skewed_tick_clock.h"
51 #include "media/cast/test/utility/audio_utility.h"
52 #include "media/cast/test/utility/default_config.h"
53 #include "media/cast/test/utility/test_util.h"
54 #include "media/cast/test/utility/udp_proxy.h"
55 #include "media/cast/test/utility/video_utility.h"
56 #include "media/cast/transport/cast_transport_config.h"
57 #include "media/cast/transport/cast_transport_defines.h"
58 #include "media/cast/transport/cast_transport_sender.h"
59 #include "media/cast/transport/cast_transport_sender_impl.h"
60
61 using media::cast::proto::IPPModel;
62 using media::cast::proto::NetworkSimulationModel;
63 using media::cast::proto::NetworkSimulationModelType;
64
65 namespace media {
66 namespace cast {
67 namespace {
68 const int kTargetDelay = 300;
69 const char kSourcePath[] = "source";
70 const char kModelPath[] = "model-path";
Alpha Left Google 2014/07/01 21:50:37 nit: Just be "model".
imcheng 2014/07/02 00:10:08 Done.
71 const char kOutputPath[] = "output";
72 const char kSimulationId[] = "sim-id";
73 const char kFfmpegLibDir[] = "ffmpeg-lib-dir";
Alpha Left Google 2014/07/01 21:50:38 nit: Just be "lib".
imcheng 2014/07/02 00:10:08 Changed to lib-dir since it needs to be directory.
74
75 void UpdateCastTransportStatus(transport::CastTransportStatus status) {
76 LOG(INFO) << "Cast transport status: " << status;
77 }
78
79 void AudioInitializationStatus(CastInitializationStatus status) {
80 LOG(INFO) << "Audio status: " << status;
81 }
82
83 void VideoInitializationStatus(CastInitializationStatus status) {
84 LOG(INFO) << "Video status: " << status;
85 }
86
87 void LogTransportEvents(const scoped_refptr<CastEnvironment>& env,
88 const std::vector<PacketEvent>& packet_events) {
89 for (std::vector<media::cast::PacketEvent>::const_iterator it =
90 packet_events.begin();
91 it != packet_events.end();
92 ++it) {
93 env->Logging()->InsertPacketEvent(it->timestamp,
94 it->type,
95 it->media_type,
96 it->rtp_timestamp,
97 it->frame_id,
98 it->packet_id,
99 it->max_packet_id,
100 it->size);
101 }
102 }
103
104 void GotVideoFrame(
105 int* counter,
106 CastReceiver* cast_receiver,
107 const scoped_refptr<media::VideoFrame>& video_frame,
108 const base::TimeTicks& render_time,
109 bool continuous) {
110 ++*counter;
111 cast_receiver->RequestDecodedVideoFrame(
112 base::Bind(&GotVideoFrame, counter, cast_receiver));
113 }
114
115 void GotAudioFrame(
116 int* counter,
117 CastReceiver* cast_receiver,
118 scoped_ptr<AudioBus> audio_bus,
119 const base::TimeTicks& playout_time,
120 bool is_continuous) {
121 ++*counter;
122 cast_receiver->RequestDecodedAudioFrame(
123 base::Bind(&GotAudioFrame, counter, cast_receiver));
124 }
125
126 void AppendLog(EncodingEventSubscriber* subscriber,
127 const std::string& extra_data,
128 const base::FilePath& output_path) {
129 media::cast::proto::LogMetadata metadata;
130 metadata.set_extra_data(extra_data);
131
132 media::cast::FrameEventList frame_events;
133 media::cast::PacketEventList packet_events;
134 subscriber->GetEventsAndReset(
135 &metadata, &frame_events, &packet_events);
136 media::cast::proto::GeneralDescription* gen_desc =
137 metadata.mutable_general_description();
138 gen_desc->set_product("Cast Simulator");
139 gen_desc->set_product_version("0.1");
140
141 scoped_ptr<char[]> serialized_log(new char[media::cast::kMaxSerializedBytes]);
142 int output_bytes;
143 bool success = media::cast::SerializeEvents(metadata,
144 frame_events,
145 packet_events,
146 true,
147 media::cast::kMaxSerializedBytes,
148 serialized_log.get(),
149 &output_bytes);
150
151 if (!success) {
152 LOG(ERROR) << "Failed to serialize log.";
153 return;
154 }
155
156 if (AppendToFile(output_path, serialized_log.get(), output_bytes) == -1) {
157 LOG(ERROR) << "Failed to append to log.";
158 }
159 }
160
161 // Run simulation once.
162 //
163 // |output_path| is the path to write serialized log.
164 // |extra_data| is extra tagging information to write to log.
165 void RunSimulation(const base::FilePath& source_path,
166 const base::FilePath& output_path,
167 const std::string& extra_data,
168 const NetworkSimulationModel& model) {
169 // Fake clock. Make sure start time is non zero.
170 base::SimpleTestTickClock testing_clock;
171 testing_clock.Advance(base::TimeDelta::FromSeconds(1));
172
173 // Task runner.
174 scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner =
175 new test::FakeSingleThreadTaskRunner(&testing_clock);
176 base::ThreadTaskRunnerHandle task_runner_handle(task_runner);
177
178 // CastEnvironments.
179 scoped_refptr<CastEnvironment> sender_env =
180 new CastEnvironment(
181 scoped_ptr<base::TickClock>(
182 new test::SkewedTickClock(&testing_clock)).Pass(),
183 task_runner,
184 task_runner,
185 task_runner);
186 scoped_refptr<CastEnvironment> receiver_env =
187 new CastEnvironment(
188 scoped_ptr<base::TickClock>(
189 new test::SkewedTickClock(&testing_clock)).Pass(),
190 task_runner,
191 task_runner,
192 task_runner);
193
194 // Event subscriber. Store at most 1 hour of events.
195 EncodingEventSubscriber audio_event_subscriber(AUDIO_EVENT,
196 100 * 60 * 60);
197 EncodingEventSubscriber video_event_subscriber(VIDEO_EVENT,
198 30 * 60 * 60);
199 sender_env->Logging()->AddRawEventSubscriber(&audio_event_subscriber);
200 sender_env->Logging()->AddRawEventSubscriber(&video_event_subscriber);
201
202 // Audio sender config.
203 AudioSenderConfig audio_sender_config = GetDefaultAudioSenderConfig();
204 audio_sender_config.target_playout_delay =
205 base::TimeDelta::FromMilliseconds(kTargetDelay);
206
207 // Audio receiver config.
208 FrameReceiverConfig audio_receiver_config =
209 GetDefaultAudioReceiverConfig();
210 audio_receiver_config.rtp_max_delay_ms =
211 audio_sender_config.target_playout_delay.InMilliseconds();
212
213 // Video sender config.
214 VideoSenderConfig video_sender_config = GetDefaultVideoSenderConfig();
215 video_sender_config.max_bitrate = 4000000;
216 video_sender_config.min_bitrate = 2000000;
217 video_sender_config.start_bitrate = 4000000;
218 video_sender_config.target_playout_delay =
219 base::TimeDelta::FromMilliseconds(kTargetDelay);
220
221 // Video receiver config.
222 FrameReceiverConfig video_receiver_config =
223 GetDefaultVideoReceiverConfig();
224 video_receiver_config.rtp_max_delay_ms =
225 video_sender_config.target_playout_delay.InMilliseconds();
226
227 // Loopback transport.
228 LoopBackTransport receiver_to_sender(receiver_env);
229 LoopBackTransport sender_to_receiver(sender_env);
230
231 // Cast receiver.
232 scoped_ptr<CastReceiver> cast_receiver(
233 CastReceiver::Create(receiver_env,
234 audio_receiver_config,
235 video_receiver_config,
236 &receiver_to_sender));
237
238 // Cast sender and transport sender.
239 scoped_ptr<transport::CastTransportSender> transport_sender(
240 new transport::CastTransportSenderImpl(
241 NULL,
242 &testing_clock,
243 net::IPEndPoint(),
244 base::Bind(&UpdateCastTransportStatus),
245 base::Bind(&LogTransportEvents, sender_env),
246 base::TimeDelta::FromSeconds(1),
247 task_runner,
248 &sender_to_receiver));
249 scoped_ptr<CastSender> cast_sender(
250 CastSender::Create(sender_env, transport_sender.get()));
251
252 // Build packet pipe.
253 if (model.type() != media::cast::proto::INTERRUPTED_POISSON_PROCESS) {
254 LOG(ERROR) << "Unknown model type " << model.type() << ".";
Alpha Left Google 2014/07/01 21:50:37 If not model just use a default of say: avg = 0.60
imcheng 2014/07/02 00:10:08 Done. Using previous model.
255 return;
256 }
257
258 const IPPModel& ipp_model = model.ipp();
259
260 std::vector<double> average_rates(ipp_model.average_rate_size());
261 std::copy(ipp_model.average_rate().begin(), ipp_model.average_rate().end(),
262 average_rates.begin());
263 test::InterruptedPoissonProcess ipp(average_rates,
264 ipp_model.coef_burstiness(), ipp_model.coef_variance(), 0);
265
266 // Connect sender to receiver. This initializes the pipe.
267 receiver_to_sender.Initialize(
268 ipp.NewBuffer(128 * 1024), cast_sender->packet_receiver(), task_runner,
269 &testing_clock);
270 sender_to_receiver.Initialize(
271 ipp.NewBuffer(128 * 1024), cast_receiver->packet_receiver(), task_runner,
272 &testing_clock);
273
274 // Start receiver.
275 int audio_frame_count = 0;
276 int video_frame_count = 0;
277 cast_receiver->RequestDecodedVideoFrame(
278 base::Bind(&GotVideoFrame, &video_frame_count, cast_receiver.get()));
279 cast_receiver->RequestDecodedAudioFrame(
280 base::Bind(&GotAudioFrame, &audio_frame_count, cast_receiver.get()));
281
282 FakeMediaSource media_source(task_runner,
283 &testing_clock,
284 video_sender_config);
285
286 // Initializing audio and video senders.
287 cast_sender->InitializeAudio(audio_sender_config,
288 base::Bind(&AudioInitializationStatus));
289 cast_sender->InitializeVideo(media_source.get_video_config(),
290 base::Bind(&VideoInitializationStatus),
291 CreateDefaultVideoEncodeAcceleratorCallback(),
292 CreateDefaultVideoEncodeMemoryCallback());
293
294 // Start sending.
295 if (!source_path.empty()) {
296 // 0 means using the FPS from the file.
297 media_source.SetSourceFile(source_path, 0);
298 }
299 media_source.Start(cast_sender->audio_frame_input(),
300 cast_sender->video_frame_input());
301
302 // Run for 3 minutes.
303 base::TimeDelta elapsed_time;
304 while (elapsed_time.InMinutes() < 3) {
305 // Each step is 100us.
306 base::TimeDelta step = base::TimeDelta::FromMicroseconds(100);
307 task_runner->Sleep(step);
308 elapsed_time += step;
309 }
310
311 LOG(INFO) << "Audio frame count: " << audio_frame_count;
312 LOG(INFO) << "Video frame count: " << video_frame_count;
313 LOG(INFO) << "Writing log: " << output_path.value();
314
315 // Truncate file and then write serialized log.
316 {
317 base::ScopedFILE file(base::OpenFile(output_path, "wb"));
318 if (!file.get()) {
319 LOG(INFO) << "Cannot write to log.";
320 return;
321 }
322 }
323 AppendLog(&video_event_subscriber, extra_data, output_path);
324 AppendLog(&audio_event_subscriber, extra_data, output_path);
325 }
326
327 bool IsModelValid(const NetworkSimulationModel& model) {
328 if (!model.has_type())
329 return false;
330 NetworkSimulationModelType type = model.type();
331 if (type == media::cast::proto::INTERRUPTED_POISSON_PROCESS) {
332 if (!model.has_ipp())
333 return false;
334 const IPPModel& ipp = model.ipp();
335 if (ipp.coef_burstiness() <= 0.0 || ipp.coef_variance() <= 0.0)
336 return false;
337 if (ipp.average_rate_size() == 0)
338 return false;
339 for (int i = 0; i < ipp.average_rate_size(); i++) {
340 if (ipp.average_rate(i) <= 0.0)
341 return false;
342 }
343 }
344
345 return true;
346 }
347
348 } // namespace
349 } // namespace cast
350 } // namespace media
24 351
25 int main(int argc, char** argv) { 352 int main(int argc, char** argv) {
26 base::AtExitManager at_exit; 353 base::AtExitManager at_exit;
27 CommandLine::Init(argc, argv); 354 CommandLine::Init(argc, argv);
28 InitLogging(logging::LoggingSettings()); 355 InitLogging(logging::LoggingSettings());
29 356
30 const CommandLine* cmd = CommandLine::ForCurrentProcess(); 357 const CommandLine* cmd = CommandLine::ForCurrentProcess();
31 base::FilePath output_path = cmd->GetSwitchValuePath(kOutputPath); 358 base::FilePath media_path =
32 CHECK(!output_path.empty()); 359 cmd->GetSwitchValuePath(media::cast::kFfmpegLibDir);
33 std::string sim_run_id = cmd->GetSwitchValueASCII(kSimulationRunId); 360 if (media_path.empty()) {
361 if (!PathService::Get(base::DIR_MODULE, &media_path)) {
362 LOG(ERROR) << "Failed to load FFmpeg.";
363 return 1;
364 }
365 }
34 366
35 std::string msg = "Log from simulation run " + sim_run_id; 367 if (!media::InitializeMediaLibrary(media_path)) {
36 int ret = base::WriteFile(output_path, &msg[0], msg.size()); 368 LOG(ERROR) << "Failed to initialize FFmpeg.";
37 if (ret != static_cast<int>(msg.size())) 369 return 1;
38 VLOG(0) << "Failed to write logs to file."; 370 }
39 371
372 base::FilePath source_path = cmd->GetSwitchValuePath(
373 media::cast::kSourcePath);
374 base::FilePath output_path = cmd->GetSwitchValuePath(
375 media::cast::kOutputPath);
376 if (output_path.empty()) {
377 base::GetTempDir(&output_path);
378 output_path = output_path.AppendASCII("sim-events.gz");
379 }
380 std::string sim_id = cmd->GetSwitchValueASCII(media::cast::kSimulationId);
381
382 base::FilePath model_path = cmd->GetSwitchValuePath(media::cast::kModelPath);
383 if (model_path.empty()) {
384 LOG(ERROR) << "Model path not set.";
385 return 1;
386 }
387 std::string model_str;
388 if (!base::ReadFileToString(model_path, &model_str)) {
389 LOG(ERROR) << "Failed to read model file.";
390 return 1;
391 }
392
393 NetworkSimulationModel model;
394 if (!model.ParseFromString(model_str)) {
395 LOG(ERROR) << "Failed to parse model.";
396 return 1;
397 }
398 if (!media::cast::IsModelValid(model)) {
399 LOG(ERROR) << "Invalid model.";
400 return 1;
401 }
402
403 base::DictionaryValue values;
404 values.SetBoolean("sim", true);
405 values.SetString("sim-id", sim_id);
406
407 std::string extra_data;
408 base::JSONWriter::Write(&values, &extra_data);
409
410 // Run.
411 media::cast::RunSimulation(source_path, output_path, extra_data, model);
40 return 0; 412 return 0;
41 } 413 }
OLDNEW
« no previous file with comments | « media/cast/test/proto/network_simulation_model.proto ('k') | media/cast/test/utility/default_config.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698