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
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/path_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/windows_version.h"
10 #include "chrome/browser/media/media_browsertest.h"
11 #include "chrome/browser/media/test_license_server.h"
12 #include "chrome/browser/media/wv_test_license_server_config.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "content/public/test/browser_test_utils.h"
17 #if defined(OS_ANDROID)
18 #include "base/android/build_info.h"
21 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
23 #if defined(ENABLE_PEPPER_CDMS)
24 // Platform-specific filename relative to the chrome executable.
25 const char kClearKeyCdmAdapterFileName[] =
26 #if defined(OS_MACOSX)
27 "clearkeycdmadapter.plugin";
29 "clearkeycdmadapter.dll";
30 #elif defined(OS_POSIX)
31 "libclearkeycdmadapter.so";
34 const char kClearKeyCdmPluginMimeType[] = "application/x-ppapi-clearkey-cdm";
35 #endif // defined(ENABLE_PEPPER_CDMS)
37 // Available key systems.
38 const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey";
39 const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
40 const char kExternalClearKeyDecryptOnlyKeySystem[] =
41 "org.chromium.externalclearkey.decryptonly";
42 const char kExternalClearKeyFileIOTestKeySystem[] =
43 "org.chromium.externalclearkey.fileiotest";
44 const char kExternalClearKeyInitializeFailKeySystem[] =
45 "org.chromium.externalclearkey.initializefail";
46 const char kExternalClearKeyCrashKeySystem[] =
47 "org.chromium.externalclearkey.crash";
49 // Supported media types.
50 const char kWebMAudioOnly[] = "audio/webm; codecs=\"vorbis\"";
51 const char kWebMVideoOnly[] = "video/webm; codecs=\"vp8\"";
52 const char kWebMAudioVideo[] = "video/webm; codecs=\"vorbis, vp8\"";
53 #if defined(USE_PROPRIETARY_CODECS)
54 const char kMP4AudioOnly[] = "audio/mp4; codecs=\"mp4a.40.2\"";
55 const char kMP4VideoOnly[] = "video/mp4; codecs=\"avc1.4D4041\"";
56 #endif // defined(USE_PROPRIETARY_CODECS)
59 const char kNoSessionToLoad[] = "";
60 const char kLoadableSession[] = "LoadableSession";
61 const char kUnknownSession[] = "UnknownSession";
63 // EME-specific test results and errors.
64 const char kEmeKeyError[] = "KEYERROR";
65 const char kEmeNotSupportedError[] = "NOTSUPPORTEDERROR";
66 const char kFileIOTestSuccess[] = "FILEIOTESTSUCCESS";
68 // The type of video src used to load media.
74 // MSE is available on all desktop platforms and on Android 4.1 and later.
75 static bool IsMSESupported() {
76 #if defined(OS_ANDROID)
77 if (base::android::BuildInfo::GetInstance()->sdk_int() < 16) {
78 VLOG(0) << "MSE is only supported in Android 4.1 and later.";
81 #endif // defined(OS_ANDROID)
85 static bool IsParentKeySystemOf(const std::string& parent_key_system,
86 const std::string& key_system) {
87 std::string prefix = parent_key_system + '.';
88 return key_system.substr(0, prefix.size()) == prefix;
91 // Base class for encrypted media tests.
92 class EncryptedMediaTestBase : public MediaBrowserTest {
94 EncryptedMediaTestBase() : is_pepper_cdm_registered_(false) {}
96 bool IsExternalClearKey(const std::string& key_system) {
97 return key_system == kExternalClearKeyKeySystem ||
98 IsParentKeySystemOf(kExternalClearKeyKeySystem, key_system);
101 #if defined(WIDEVINE_CDM_AVAILABLE)
102 bool IsWidevine(const std::string& key_system) {
103 return key_system == kWidevineKeySystem;
105 #endif // defined(WIDEVINE_CDM_AVAILABLE)
107 void RunEncryptedMediaTestPage(const std::string& html_page,
108 const std::string& key_system,
109 std::vector<StringPair>* query_params,
110 const std::string& expected_title) {
111 StartLicenseServerIfNeeded(key_system, query_params);
112 RunMediaTestPage(html_page, query_params, expected_title, true);
115 // Tests |html_page| using |media_file| (with |media_type|) and |key_system|.
116 // When |session_to_load| is not empty, the test will try to load
117 // |session_to_load| with stored keys, instead of creating a new session
118 // and trying to update it with licenses.
119 // When |force_invalid_response| is true, the test will provide invalid
120 // responses, which should trigger errors.
121 // TODO(xhwang): Find an easier way to pass multiple configuration test
123 void RunEncryptedMediaTest(const std::string& html_page,
124 const std::string& media_file,
125 const std::string& media_type,
126 const std::string& key_system,
128 const std::string& session_to_load,
129 bool force_invalid_response,
130 const std::string& expected_title) {
131 if (src_type == MSE && !IsMSESupported()) {
132 VLOG(0) << "Skipping test - MSE not supported.";
135 std::vector<StringPair> query_params;
136 query_params.push_back(std::make_pair("mediaFile", media_file));
137 query_params.push_back(std::make_pair("mediaType", media_type));
138 query_params.push_back(std::make_pair("keySystem", key_system));
140 query_params.push_back(std::make_pair("useMSE", "1"));
141 if (force_invalid_response)
142 query_params.push_back(std::make_pair("forceInvalidResponse", "1"));
143 if (!session_to_load.empty())
144 query_params.push_back(std::make_pair("sessionToLoad", session_to_load));
145 RunEncryptedMediaTestPage(html_page, key_system, &query_params,
149 void RunSimpleEncryptedMediaTest(const std::string& media_file,
150 const std::string& media_type,
151 const std::string& key_system,
153 std::string expected_title = kEnded;
154 if (!IsPlayBackPossible(key_system))
155 expected_title = kEmeKeyError;
157 RunEncryptedMediaTest("encrypted_media_player.html", media_file, media_type,
158 key_system, src_type, kNoSessionToLoad, false,
160 // Check KeyMessage received for all key systems.
161 bool receivedKeyMessage = false;
162 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
163 browser()->tab_strip_model()->GetActiveWebContents(),
164 "window.domAutomationController.send(video.receivedKeyMessage);",
165 &receivedKeyMessage));
166 EXPECT_TRUE(receivedKeyMessage);
170 void StartLicenseServerIfNeeded(const std::string& key_system,
171 std::vector<StringPair>* query_params) {
172 scoped_ptr<TestLicenseServerConfig> config = GetServerConfig(key_system);
175 license_server_.reset(new TestLicenseServer(config.Pass()));
176 EXPECT_TRUE(license_server_->Start());
177 query_params->push_back(std::make_pair("licenseServerURL",
178 license_server_->GetServerURL()));
181 bool IsPlayBackPossible(const std::string& key_system) {
182 #if defined(WIDEVINE_CDM_AVAILABLE)
183 if (IsWidevine(key_system) && !GetServerConfig(key_system))
185 #endif // defined(WIDEVINE_CDM_AVAILABLE)
189 scoped_ptr<TestLicenseServerConfig> GetServerConfig(
190 const std::string& key_system) {
191 #if defined(WIDEVINE_CDM_AVAILABLE)
192 if (IsWidevine(key_system)) {
193 scoped_ptr<TestLicenseServerConfig> config =
194 scoped_ptr<TestLicenseServerConfig>(new WVTestLicenseServerConfig());
195 if (config->IsPlatformSupported())
196 return config.Pass();
198 #endif // defined(WIDEVINE_CDM_AVAILABLE)
199 return scoped_ptr<TestLicenseServerConfig>();
203 scoped_ptr<TestLicenseServer> license_server_;
205 // We want to fail quickly when a test fails because an error is encountered.
206 virtual void AddWaitForTitles(content::TitleWatcher* title_watcher) OVERRIDE {
207 MediaBrowserTest::AddWaitForTitles(title_watcher);
208 title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kEmeNotSupportedError));
209 title_watcher->AlsoWaitForTitle(base::ASCIIToUTF16(kEmeKeyError));
212 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
213 #if defined(OS_ANDROID)
214 command_line->AppendSwitch(
215 switches::kDisableGestureRequirementForMediaPlayback);
216 #endif // defined(OS_ANDROID)
219 void SetUpCommandLineForKeySystem(const std::string& key_system,
220 CommandLine* command_line) {
221 if (GetServerConfig(key_system))
222 // Since the web and license servers listen on different ports, we need to
223 // disable web-security to send license requests to the license server.
224 // TODO(shadi): Add port forwarding to the test web server configuration.
225 command_line->AppendSwitch(switches::kDisableWebSecurity);
227 #if defined(ENABLE_PEPPER_CDMS)
228 if (IsExternalClearKey(key_system)) {
229 RegisterPepperCdm(command_line, kClearKeyCdmAdapterFileName, key_system);
231 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
232 else if (IsWidevine(key_system)) {
233 RegisterPepperCdm(command_line, kWidevineCdmAdapterFileName, key_system);
235 #endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
236 #endif // defined(ENABLE_PEPPER_CDMS)
240 #if defined(ENABLE_PEPPER_CDMS)
241 void RegisterPepperCdm(CommandLine* command_line,
242 const std::string& adapter_name,
243 const std::string& key_system) {
244 DCHECK(!is_pepper_cdm_registered_)
245 << "RegisterPepperCdm() can only be called once.";
246 is_pepper_cdm_registered_ = true;
248 // Append the switch to register the Clear Key CDM Adapter.
249 base::FilePath plugin_dir;
250 EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &plugin_dir));
251 base::FilePath plugin_lib = plugin_dir.AppendASCII(adapter_name);
252 EXPECT_TRUE(base::PathExists(plugin_lib)) << plugin_lib.value();
253 base::FilePath::StringType pepper_plugin = plugin_lib.value();
254 pepper_plugin.append(FILE_PATH_LITERAL("#CDM#0.1.0.0;"));
256 pepper_plugin.append(base::ASCIIToWide(GetPepperType(key_system)));
258 pepper_plugin.append(GetPepperType(key_system));
260 command_line->AppendSwitchNative(switches::kRegisterPepperPlugins,
264 // Adapted from key_systems.cc.
265 std::string GetPepperType(const std::string& key_system) {
266 if (IsExternalClearKey(key_system))
267 return kClearKeyCdmPluginMimeType;
268 #if defined(WIDEVINE_CDM_AVAILABLE)
269 if (IsWidevine(key_system))
270 return kWidevineCdmPluginMimeType;
271 #endif // WIDEVINE_CDM_AVAILABLE
276 #endif // defined(ENABLE_PEPPER_CDMS)
278 bool is_pepper_cdm_registered_;
281 #if defined(ENABLE_PEPPER_CDMS)
282 // Tests encrypted media playback using ExternalClearKey key system in
283 // decrypt-and-decode mode.
284 class ECKEncryptedMediaTest : public EncryptedMediaTestBase {
286 // We use special |key_system| names to do non-playback related tests, e.g.
287 // kExternalClearKeyFileIOTestKeySystem is used to test file IO.
288 void TestNonPlaybackCases(const std::string& key_system,
289 const std::string& expected_title) {
290 // Since we do not test playback, arbitrarily choose a test file and source
292 RunEncryptedMediaTest("encrypted_media_player.html",
303 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
304 EncryptedMediaTestBase::SetUpCommandLine(command_line);
305 SetUpCommandLineForKeySystem(kExternalClearKeyKeySystem, command_line);
309 #if defined(WIDEVINE_CDM_AVAILABLE)
310 // Tests encrypted media playback using Widevine key system.
311 class WVEncryptedMediaTest : public EncryptedMediaTestBase {
313 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
314 EncryptedMediaTestBase::SetUpCommandLine(command_line);
315 SetUpCommandLineForKeySystem(kWidevineKeySystem, command_line);
319 #endif // defined(WIDEVINE_CDM_AVAILABLE)
320 #endif // defined(ENABLE_PEPPER_CDMS)
322 // Tests encrypted media playback with a combination of parameters:
323 // - char*: Key system name.
324 // - bool: True to load media using MSE, otherwise use src.
326 // Note: Only parameterized (*_P) tests can be used. Non-parameterized (*_F)
327 // tests will crash at GetParam(). To add non-parameterized tests, use
328 // EncryptedMediaTestBase or one of its subclasses (e.g. WVEncryptedMediaTest).
329 class EncryptedMediaTest : public EncryptedMediaTestBase,
330 public testing::WithParamInterface<std::tr1::tuple<const char*, SrcType> > {
332 std::string CurrentKeySystem() {
333 return std::tr1::get<0>(GetParam());
336 SrcType CurrentSourceType() {
337 return std::tr1::get<1>(GetParam());
340 void TestSimplePlayback(const std::string& encrypted_media,
341 const std::string& media_type) {
342 RunSimpleEncryptedMediaTest(
343 encrypted_media, media_type, CurrentKeySystem(), CurrentSourceType());
346 void RunInvalidResponseTest() {
347 RunEncryptedMediaTest("encrypted_media_player.html",
348 "bear-320x240-av-enc_av.webm",
357 void TestFrameSizeChange() {
358 RunEncryptedMediaTest("encrypted_frame_size_change.html",
359 "frame_size_change-av-enc-v.webm",
368 void TestConfigChange() {
369 DCHECK(IsMSESupported());
370 std::vector<StringPair> query_params;
371 query_params.push_back(std::make_pair("keySystem", CurrentKeySystem()));
372 query_params.push_back(std::make_pair("runEncrypted", "1"));
373 RunEncryptedMediaTestPage("mse_config_change.html",
380 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
381 EncryptedMediaTestBase::SetUpCommandLine(command_line);
382 SetUpCommandLineForKeySystem(CurrentKeySystem(), command_line);
386 using ::testing::Combine;
387 using ::testing::Values;
389 #if !defined(OS_ANDROID)
390 INSTANTIATE_TEST_CASE_P(SRC_ClearKey, EncryptedMediaTest,
391 Combine(Values(kClearKeyKeySystem), Values(SRC)));
392 #endif // !defined(OS_ANDROID)
394 INSTANTIATE_TEST_CASE_P(MSE_ClearKey, EncryptedMediaTest,
395 Combine(Values(kClearKeyKeySystem), Values(MSE)));
397 // External Clear Key is currently only used on platforms that use Pepper CDMs.
398 #if defined(ENABLE_PEPPER_CDMS)
399 INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey, EncryptedMediaTest,
400 Combine(Values(kExternalClearKeyKeySystem), Values(SRC)));
401 INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey, EncryptedMediaTest,
402 Combine(Values(kExternalClearKeyKeySystem), Values(MSE)));
403 // To reduce test time, only run ExternalClearKeyDecryptOnly with MSE.
404 INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKeyDecryptOnly, EncryptedMediaTest,
405 Combine(Values(kExternalClearKeyDecryptOnlyKeySystem), Values(MSE)));
406 #endif // defined(ENABLE_PEPPER_CDMS)
408 #if defined(WIDEVINE_CDM_AVAILABLE)
409 // This test doesn't fully test playback with Widevine. So we only run Widevine
410 // test with MSE (no SRC) to reduce test time. Also, on Android EME only works
411 // with MSE and we cannot run this test with SRC.
412 INSTANTIATE_TEST_CASE_P(MSE_Widevine, EncryptedMediaTest,
413 Combine(Values(kWidevineKeySystem), Values(MSE)));
414 #endif // defined(WIDEVINE_CDM_AVAILABLE)
416 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_WebM) {
417 TestSimplePlayback("bear-a-enc_a.webm", kWebMAudioOnly);
420 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioClearVideo_WebM) {
421 TestSimplePlayback("bear-320x240-av-enc_a.webm", kWebMAudioVideo);
424 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoAudio_WebM) {
425 TestSimplePlayback("bear-320x240-av-enc_av.webm", kWebMAudioVideo);
428 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_WebM) {
429 TestSimplePlayback("bear-320x240-v-enc_v.webm", kWebMVideoOnly);
432 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoClearAudio_WebM) {
433 TestSimplePlayback("bear-320x240-av-enc_v.webm", kWebMAudioVideo);
436 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, InvalidResponseKeyError) {
437 RunInvalidResponseTest();
440 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, ConfigChangeVideo) {
441 if (CurrentSourceType() != MSE || !IsMSESupported()) {
442 VLOG(0) << "Skipping test - ConfigChange test requires MSE.";
445 if (!IsPlayBackPossible(CurrentKeySystem())) {
446 VLOG(0) << "Skipping test - ConfigChange test requires video playback.";
452 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, FrameSizeChangeVideo) {
453 // Times out on Windows XP. http://crbug.com/171937
455 if (base::win::GetVersion() < base::win::VERSION_VISTA)
458 if (!IsPlayBackPossible(CurrentKeySystem())) {
459 VLOG(0) << "Skipping test - FrameSizeChange test requires video playback.";
462 TestFrameSizeChange();
465 #if defined(USE_PROPRIETARY_CODECS)
466 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4) {
467 // MP4 without MSE is not support yet, http://crbug.com/170793.
468 if (CurrentSourceType() != MSE) {
469 VLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
472 TestSimplePlayback("bear-640x360-v_frag-cenc.mp4", kMP4VideoOnly);
475 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4) {
476 // MP4 without MSE is not support yet, http://crbug.com/170793.
477 if (CurrentSourceType() != MSE) {
478 VLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
481 TestSimplePlayback("bear-640x360-a_frag-cenc.mp4", kMP4AudioOnly);
483 #endif // defined(USE_PROPRIETARY_CODECS)
485 #if defined(WIDEVINE_CDM_AVAILABLE)
486 // The parent key system cannot be used in generateKeyRequest.
487 IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, ParentThrowsException) {
488 RunEncryptedMediaTest("encrypted_media_player.html",
495 kEmeNotSupportedError);
497 #endif // defined(WIDEVINE_CDM_AVAILABLE)
499 #if defined(ENABLE_PEPPER_CDMS)
500 IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, InitializeCDMFail) {
501 TestNonPlaybackCases(kExternalClearKeyInitializeFailKeySystem, kEmeKeyError);
504 // When CDM crashes, we should still get a decode error.
505 IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, CDMCrashDuringDecode) {
506 TestNonPlaybackCases(kExternalClearKeyCrashKeySystem, kError);
509 IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, FileIOTest) {
510 TestNonPlaybackCases(kExternalClearKeyFileIOTestKeySystem,
514 IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadLoadableSession) {
515 RunEncryptedMediaTest("encrypted_media_player.html",
516 "bear-320x240-v-enc_v.webm",
518 kExternalClearKeyKeySystem,
525 IN_PROC_BROWSER_TEST_F(ECKEncryptedMediaTest, LoadUnknownSession) {
526 // TODO(xhwang): Add a specific error for this failure, e.g. kSessionNotFound.
527 RunEncryptedMediaTest("encrypted_media_player.html",
528 "bear-320x240-v-enc_v.webm",
530 kExternalClearKeyKeySystem,
536 #endif // defined(ENABLE_PEPPER_CDMS)