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
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/debug/trace_event_impl.h"
7 #include "base/json/json_reader.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/test/trace_event_analyzer.h"
10 #include "base/values.h"
11 #include "content/browser/media/webrtc_internals.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/public/common/content_switches.h"
14 #include "content/public/test/browser_test_utils.h"
15 #include "content/public/test/content_browser_test_utils.h"
16 #include "content/public/test/test_utils.h"
17 #include "content/shell/browser/shell.h"
18 #include "content/test/webrtc_content_browsertest_base.h"
19 #include "net/test/embedded_test_server/embedded_test_server.h"
20 #include "testing/perf/perf_test.h"
23 #include "base/win/windows_version.h"
26 using trace_analyzer::TraceAnalyzer;
27 using trace_analyzer::Query;
28 using trace_analyzer::TraceEventVector;
32 static const char kGetUserMediaAndStop[] = "getUserMediaAndStop";
33 static const char kGetUserMediaAndGetStreamUp[] = "getUserMediaAndGetStreamUp";
34 static const char kGetUserMediaAndAnalyseAndStop[] =
35 "getUserMediaAndAnalyseAndStop";
36 static const char kGetUserMediaAndExpectFailure[] =
37 "getUserMediaAndExpectFailure";
38 static const char kRenderSameTrackMediastreamAndStop[] =
39 "renderSameTrackMediastreamAndStop";
40 static const char kRenderClonedMediastreamAndStop[] =
41 "renderClonedMediastreamAndStop";
42 static const char kRenderClonedTrackMediastreamAndStop[] =
43 "renderClonedTrackMediastreamAndStop";
44 static const char kRenderDuplicatedMediastreamAndStop[] =
45 "renderDuplicatedMediastreamAndStop";
47 // Results returned by JS.
48 static const char kOK[] = "OK";
50 std::string GenerateGetUserMediaWithMandatorySourceID(
51 const std::string& function_name,
52 const std::string& audio_source_id,
53 const std::string& video_source_id) {
54 const std::string audio_constraint =
55 "audio: {mandatory: { sourceId:\"" + audio_source_id + "\"}}, ";
57 const std::string video_constraint =
58 "video: {mandatory: { sourceId:\"" + video_source_id + "\"}}";
59 return function_name + "({" + audio_constraint + video_constraint + "});";
62 std::string GenerateGetUserMediaWithOptionalSourceID(
63 const std::string& function_name,
64 const std::string& audio_source_id,
65 const std::string& video_source_id) {
66 const std::string audio_constraint =
67 "audio: {optional: [{sourceId:\"" + audio_source_id + "\"}]}, ";
69 const std::string video_constraint =
70 "video: {optional: [{ sourceId:\"" + video_source_id + "\"}]}";
71 return function_name + "({" + audio_constraint + video_constraint + "});";
78 class WebRtcGetUserMediaBrowserTest: public WebRtcContentBrowserTest,
79 public testing::WithParamInterface<bool> {
81 WebRtcGetUserMediaBrowserTest() : trace_log_(NULL) {}
82 virtual ~WebRtcGetUserMediaBrowserTest() {}
84 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
85 WebRtcContentBrowserTest::SetUpCommandLine(command_line);
87 bool enable_audio_track_processing = GetParam();
88 if (enable_audio_track_processing)
89 command_line->AppendSwitch(switches::kEnableAudioTrackProcessing);
93 CHECK(trace_log_ == NULL) << "Can only can start tracing once";
94 trace_log_ = base::debug::TraceLog::GetInstance();
95 trace_log_->SetEnabled(base::debug::CategoryFilter("video"),
96 base::debug::TraceLog::RECORDING_MODE,
97 base::debug::TraceLog::ENABLE_SAMPLING);
98 // Check that we are indeed recording.
99 EXPECT_EQ(trace_log_->GetNumTracesRecorded(), 1);
103 CHECK(message_loop_runner_ == NULL) << "Calling StopTracing more than once";
104 trace_log_->SetDisabled();
105 message_loop_runner_ = new MessageLoopRunner;
106 trace_log_->Flush(base::Bind(
107 &WebRtcGetUserMediaBrowserTest::OnTraceDataCollected,
108 base::Unretained(this)));
109 message_loop_runner_->Run();
112 void OnTraceDataCollected(
113 const scoped_refptr<base::RefCountedString>& events_str_ptr,
114 bool has_more_events) {
115 CHECK(!has_more_events);
116 recorded_trace_data_ = events_str_ptr;
117 message_loop_runner_->Quit();
120 TraceAnalyzer* CreateTraceAnalyzer() {
121 return TraceAnalyzer::Create("[" + recorded_trace_data_->data() + "]");
124 void RunGetUserMediaAndCollectMeasures(const int time_to_sample_secs,
125 const std::string& measure_filter,
126 const std::string& graph_name) {
127 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
129 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
130 NavigateToURL(shell(), url);
132 // Put getUserMedia to work and let it run for a couple of seconds.
133 DCHECK(time_to_sample_secs);
134 ExecuteJavascriptAndWaitForOk(
135 base::StringPrintf("%s({video: true});",
136 kGetUserMediaAndGetStreamUp));
138 // Now the stream is up and running, start collecting traces.
141 // Let the stream run for a while in javascript.
142 ExecuteJavascriptAndWaitForOk(
143 base::StringPrintf("waitAndStopVideoTrack(%d);", time_to_sample_secs));
145 // Wait until the page title changes to "OK". Do not sleep() here since that
146 // would stop both this code and the browser underneath.
149 scoped_ptr<TraceAnalyzer> analyzer(CreateTraceAnalyzer());
150 analyzer->AssociateBeginEndEvents();
151 trace_analyzer::TraceEventVector events;
152 DCHECK(measure_filter.size());
153 analyzer->FindEvents(
154 Query::EventNameIs(measure_filter),
156 ASSERT_GT(events.size(), 0u)
157 << "Could not collect any samples during test, this is bad";
159 std::string duration_us;
160 std::string interarrival_us;
161 for (size_t i = 0; i != events.size(); ++i) {
163 base::StringPrintf("%d,", static_cast<int>(events[i]->duration)));
166 for (size_t i = 1; i < events.size(); ++i) {
167 // The event |timestamp| comes in ns, divide to get us like |duration|.
168 interarrival_us.append(base::StringPrintf("%d,",
169 static_cast<int>((events[i]->timestamp - events[i - 1]->timestamp) /
170 base::Time::kNanosecondsPerMicrosecond)));
173 perf_test::PrintResultList(
174 graph_name, "", "sample_duration", duration_us, "us", true);
176 perf_test::PrintResultList(
177 graph_name, "", "interarrival_time", interarrival_us, "us", true);
180 void GetSources(std::vector<std::string>* audio_ids,
181 std::vector<std::string>* video_ids) {
182 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
183 NavigateToURL(shell(), url);
185 std::string sources_as_json = ExecuteJavascriptAndReturnResult(
187 EXPECT_FALSE(sources_as_json.empty());
190 std::string error_message;
191 scoped_ptr<base::Value> value(
192 base::JSONReader::ReadAndReturnError(sources_as_json,
193 base::JSON_ALLOW_TRAILING_COMMAS,
197 ASSERT_TRUE(value.get() != NULL) << error_message;
198 EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);
200 base::ListValue* values;
201 ASSERT_TRUE(value->GetAsList(&values));
203 for (base::ListValue::iterator it = values->begin();
204 it != values->end(); ++it) {
205 const base::DictionaryValue* dict;
208 ASSERT_TRUE((*it)->GetAsDictionary(&dict));
209 ASSERT_TRUE(dict->GetString("kind", &kind));
210 ASSERT_TRUE(dict->GetString("id", &id));
211 ASSERT_FALSE(id.empty());
212 EXPECT_TRUE(kind == "audio" || kind == "video");
213 if (kind == "audio") {
214 audio_ids->push_back(id);
215 } else if (kind == "video") {
216 video_ids->push_back(id);
219 ASSERT_FALSE(audio_ids->empty());
220 ASSERT_FALSE(video_ids->empty());
224 base::debug::TraceLog* trace_log_;
225 scoped_refptr<base::RefCountedString> recorded_trace_data_;
226 scoped_refptr<MessageLoopRunner> message_loop_runner_;
229 static const bool kRunTestsWithFlag[] = { false, true };
230 INSTANTIATE_TEST_CASE_P(WebRtcGetUserMediaBrowserTests,
231 WebRtcGetUserMediaBrowserTest,
232 testing::ValuesIn(kRunTestsWithFlag));
234 // These tests will all make a getUserMedia call with different constraints and
235 // see that the success callback is called. If the error callback is called or
236 // none of the callbacks are called the tests will simply time out and fail.
237 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetVideoStreamAndStop) {
238 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
240 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
241 NavigateToURL(shell(), url);
243 ExecuteJavascriptAndWaitForOk(
244 base::StringPrintf("%s({video: true});", kGetUserMediaAndStop));
247 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
248 RenderSameTrackMediastreamAndStop) {
249 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
251 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
252 NavigateToURL(shell(), url);
254 ExecuteJavascriptAndWaitForOk(
255 base::StringPrintf("%s({video: true});",
256 kRenderSameTrackMediastreamAndStop));
259 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
260 RenderClonedMediastreamAndStop) {
261 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
263 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
264 NavigateToURL(shell(), url);
267 ExecuteJavascriptAndWaitForOk(
268 base::StringPrintf("%s({video: true});",
269 kRenderClonedMediastreamAndStop));
272 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
273 kRenderClonedTrackMediastreamAndStop) {
274 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
276 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
277 NavigateToURL(shell(), url);
279 ExecuteJavascriptAndWaitForOk(
280 base::StringPrintf("%s({video: true});",
281 kRenderClonedTrackMediastreamAndStop));
284 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
285 kRenderDuplicatedMediastreamAndStop) {
286 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
288 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
289 NavigateToURL(shell(), url);
291 ExecuteJavascriptAndWaitForOk(
292 base::StringPrintf("%s({video: true});",
293 kRenderDuplicatedMediastreamAndStop));
296 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
297 GetAudioAndVideoStreamAndStop) {
298 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
300 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
301 NavigateToURL(shell(), url);
303 ExecuteJavascriptAndWaitForOk(base::StringPrintf(
304 "%s({video: true, audio: true});", kGetUserMediaAndStop));
307 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
308 GetAudioAndVideoStreamAndClone) {
309 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
311 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
312 NavigateToURL(shell(), url);
314 ExecuteJavascriptAndWaitForOk("getUserMediaAndClone();");
317 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
318 RenderVideoTrackInMultipleTagsAndPause) {
319 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
321 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
322 NavigateToURL(shell(), url);
324 ExecuteJavascriptAndWaitForOk("getUserMediaAndRenderInSeveralVideoTags();");
329 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
330 GetUserMediaWithMandatorySourceID) {
331 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
333 std::vector<std::string> audio_ids;
334 std::vector<std::string> video_ids;
335 GetSources(&audio_ids, &video_ids);
337 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
339 // Test all combinations of mandatory sourceID;
340 for (std::vector<std::string>::const_iterator video_it = video_ids.begin();
341 video_it != video_ids.end(); ++video_it) {
342 for (std::vector<std::string>::const_iterator audio_it = audio_ids.begin();
343 audio_it != audio_ids.end(); ++audio_it) {
344 NavigateToURL(shell(), url);
345 EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
346 GenerateGetUserMediaWithMandatorySourceID(
347 kGetUserMediaAndStop,
354 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
355 GetUserMediaWithInvalidMandatorySourceID) {
356 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
358 std::vector<std::string> audio_ids;
359 std::vector<std::string> video_ids;
360 GetSources(&audio_ids, &video_ids);
362 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
364 // Test with invalid mandatory audio sourceID.
365 NavigateToURL(shell(), url);
366 EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
367 GenerateGetUserMediaWithMandatorySourceID(
368 kGetUserMediaAndExpectFailure,
372 // Test with invalid mandatory video sourceID.
373 EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
374 GenerateGetUserMediaWithMandatorySourceID(
375 kGetUserMediaAndExpectFailure,
377 "something invalid")));
379 // Test with empty mandatory audio sourceID.
380 EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
381 GenerateGetUserMediaWithMandatorySourceID(
382 kGetUserMediaAndExpectFailure,
387 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
388 GetUserMediaWithInvalidOptionalSourceID) {
389 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
391 std::vector<std::string> audio_ids;
392 std::vector<std::string> video_ids;
393 GetSources(&audio_ids, &video_ids);
395 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
397 // Test with invalid optional audio sourceID.
398 NavigateToURL(shell(), url);
399 EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
400 GenerateGetUserMediaWithOptionalSourceID(
401 kGetUserMediaAndStop,
405 // Test with invalid optional video sourceID.
406 EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
407 GenerateGetUserMediaWithOptionalSourceID(
408 kGetUserMediaAndStop,
410 "something invalid")));
412 // Test with empty optional audio sourceID.
413 EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
414 GenerateGetUserMediaWithOptionalSourceID(
415 kGetUserMediaAndStop,
420 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaAndStop) {
421 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
423 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
424 NavigateToURL(shell(), url);
426 ExecuteJavascriptAndWaitForOk(
427 "twoGetUserMediaAndStop({video: true, audio: true});");
430 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
431 GetUserMediaWithTooHighVideoConstraintsValues) {
432 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
434 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
436 int large_value = 99999;
437 std::string call = GenerateGetUserMediaCall(kGetUserMediaAndExpectFailure,
444 NavigateToURL(shell(), url);
446 // TODO(perkj): A proper error code should be returned by gUM.
447 EXPECT_EQ("TrackStartError", ExecuteJavascriptAndReturnResult(call));
450 // This test will make a simple getUserMedia page, verify that video is playing
451 // in a simple local <video>, and for a couple of seconds, collect some
452 // performance traces from VideoCaptureController colorspace conversion and
453 // potential resizing.
454 IN_PROC_BROWSER_TEST_P(
455 WebRtcGetUserMediaBrowserTest,
456 TraceVideoCaptureControllerPerformanceDuringGetUserMedia) {
457 RunGetUserMediaAndCollectMeasures(
459 "VideoCaptureController::OnIncomingCapturedData",
460 "VideoCaptureController");
463 // This test calls getUserMedia and checks for aspect ratio behavior.
464 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
465 TestGetUserMediaAspectRatio4To3) {
466 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
468 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
470 std::string constraints_4_3 = GenerateGetUserMediaCall(
471 kGetUserMediaAndAnalyseAndStop, 640, 640, 480, 480, 30, 30);
473 NavigateToURL(shell(), url);
474 ASSERT_EQ("w=640:h=480",
475 ExecuteJavascriptAndReturnResult(constraints_4_3));
478 // This test calls getUserMedia and checks for aspect ratio behavior.
479 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
480 TestGetUserMediaAspectRatio16To9) {
481 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
483 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
485 std::string constraints_16_9 = GenerateGetUserMediaCall(
486 kGetUserMediaAndAnalyseAndStop, 640, 640, 360, 360, 30, 30);
488 NavigateToURL(shell(), url);
489 ASSERT_EQ("w=640:h=360",
490 ExecuteJavascriptAndReturnResult(constraints_16_9));
493 // This test calls getUserMedia and checks for aspect ratio behavior.
494 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
495 TestGetUserMediaAspectRatio1To1) {
496 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
498 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
500 std::string constraints_1_1 = GenerateGetUserMediaCall(
501 kGetUserMediaAndAnalyseAndStop, 320, 320, 320, 320, 30, 30);
503 NavigateToURL(shell(), url);
504 ASSERT_EQ("w=320:h=320",
505 ExecuteJavascriptAndReturnResult(constraints_1_1));
510 struct UserMediaSizes {
521 class WebRtcConstraintsBrowserTest
522 : public WebRtcContentBrowserTest,
523 public testing::WithParamInterface<UserMediaSizes> {
525 WebRtcConstraintsBrowserTest() : user_media_(GetParam()) {}
526 const UserMediaSizes& user_media() const { return user_media_; }
529 UserMediaSizes user_media_;
532 // This test calls getUserMedia in sequence with different constraints.
533 IN_PROC_BROWSER_TEST_P(WebRtcConstraintsBrowserTest, GetUserMediaConstraints) {
534 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
536 GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
538 std::string call = GenerateGetUserMediaCall(kGetUserMediaAndStop,
539 user_media().min_width,
540 user_media().max_width,
541 user_media().min_height,
542 user_media().max_height,
543 user_media().min_frame_rate,
544 user_media().max_frame_rate);
545 DVLOG(1) << "Calling getUserMedia: " << call;
546 NavigateToURL(shell(), url);
547 ExecuteJavascriptAndWaitForOk(call);
550 static const UserMediaSizes kAllUserMediaSizes[] = {
551 {320, 320, 180, 180, 30, 30},
552 {320, 320, 240, 240, 30, 30},
553 {640, 640, 360, 360, 30, 30},
554 {640, 640, 480, 480, 30, 30},
555 {960, 960, 720, 720, 30, 30},
556 {1280, 1280, 720, 720, 30, 30},
557 {1920, 1920, 1080, 1080, 30, 30}};
559 INSTANTIATE_TEST_CASE_P(UserMedia,
560 WebRtcConstraintsBrowserTest,
561 testing::ValuesIn(kAllUserMediaSizes));
563 } // namespace content