2020/media/emeTest.js (271 lines of code) (raw):
/**
* @license
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
goog.require('goog.string');
/**
* Encrypted Media Test Suite.
* @class
*/
var EncryptedmediaTest = function() {
var emeVersion = 'Current Editor\'s Draft';
var webkitPrefix = MediaSource.prototype.version.indexOf('webkit') >= 0;
var tests = [];
var info = 'No MSE Support!';
if (window.MediaSource) {
info = 'EME Spec Version: ' + emeVersion;
info += ' | webkit prefix: ' + webkitPrefix.toString();
}
info += ' | Default Timeout: ' + TestBase.timeout + 'ms';
var fields = ['passes', 'failures', 'timeouts'];
var createEmeTest = function(
testId, name, category = 'EME', mandatory = true, streams = []) {
var t = createTest(name, category, mandatory, testId, 'EME Conformance Tests');
t.prototype.setStreams(streams);
t.prototype.index = tests.length;
t.prototype.emeHandler = new EMEHandler();
t.prototype.baseTearDown = t.prototype.teardown;
t.prototype.teardown = function(testSuiteVer, cb) {
t.prototype.emeHandler.closeAllKeySessions(function() {
t.prototype.emeHandler = new EMEHandler();
});
this.baseTearDown(testSuiteVer, cb);
};
tests.push(t);
return t;
};
/**
* Ensure encrypted video or audio could be played.
*/
var createEncryptedCodecTest = function(
testId, encStream, otherStream, keySystem, mandatory = true, desc = '') {
var keySystemTitle = goog.string.toTitleCase(keySystem);
var videoStream = encStream.mediatype == 'video' ? encStream : otherStream;
var audioStream = encStream.mediatype == 'audio' ? encStream : otherStream;
var test = createEmeTest(
testId,
`${keySystemTitle}${encStream.codec}${desc}` +
`${goog.string.toTitleCase(encStream.mediatype)}`,
`${keySystemTitle}${mandatory ? '' : ' (Optional)'}`,
mandatory,
[videoStream, audioStream]);
test.prototype.title = `Test if we can play ${encStream.mediatype} ` +
`encrypted with ${keySystemTitle} encryption.`;
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
setupMse(video, runner, videoStream, audioStream);
setupEme(runner, testEmeHandler, video, encStream, keySystem);
video.addEventListener('timeupdate', function onTimeUpdate(e) {
if (!video.paused && video.currentTime >= 15 &&
!testEmeHandler.keyUnusable) {
video.removeEventListener('timeupdate', onTimeUpdate);
runner.checkGE(video.currentTime, 15, 'currentTime');
runner.succeed();
}
});
video.play();
};
};
createEncryptedCodecTest(
'3.1.1.1', Media.H264.VideoSmallCenc, Media.AAC.AudioNormal,
LicenseManager.WIDEVINE);
createEncryptedCodecTest(
'3.1.2.1', Media.AAC.AudioSmallCenc, Media.H264.VideoNormal,
LicenseManager.WIDEVINE);
createEncryptedCodecTest(
'3.1.3.1', Media.Opus.SintelEncrypted, Media.VP9.VideoNormal,
LicenseManager.WIDEVINE);
createEncryptedCodecTest(
'3.1.4.1', Media.VP9.VideoHighEnc, Media.Opus.CarMed,
LicenseManager.WIDEVINE);
createEncryptedCodecTest(
'3.1.5.1', Media.VP9.VideoHighSubSampleEnc, Media.Opus.CarMed,
LicenseManager.WIDEVINE, true, 'Subsample');
/**
* Validate device supports key rotation with 16 MediaKeySesssion objects and
* 16 keys per MediaKeySession object.
*/
var createWidevineH264MultiMediaKeySessionsTest = function() {
var videoStream = Media.H264.VideoMultiKeyCenc;
var audioStream = Media.AAC.AudioNormal;
var test = createEmeTest(
'3.1.6.1',
'WidevineH264MultiMediaKeySessions',
'Widevine',
true,
[videoStream, audioStream]);
test.prototype.title =
'Test creating 16 MediaKeySession objects each with 16 keys for ' +
'playing encrypted with Widevine encryption.';
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
var videoStreams = [];
for (var i = 0; i < 16; i++) {
videoStreams.push(videoStream);
}
setupMse(video, runner, videoStreams, audioStream);
setupEme(
runner, testEmeHandler, video, videoStream, LicenseManager.WIDEVINE);
video.addEventListener('timeupdate', function onTimeUpdate(e) {
if (!video.paused && video.currentTime >= 15 &&
!testEmeHandler.keyUnusable) {
video.removeEventListener('timeupdate', onTimeUpdate);
runner.checkGE(video.currentTime, 15, 'currentTime');
runner.checkEq(
testEmeHandler.keySessions.length, 16, 'keySessionCount');
runner.checkEq(testEmeHandler.keyCount, 256, 'keyCount');
runner.succeed();
}
});
video.play();
};
};
createWidevineH264MultiMediaKeySessionsTest();
/**
* Ensure Widevine encrypted video could be played with no clear start and
* a 5 seconds license delay.
*/
var createWidevineLicenseDelayTest = function(testId, videoStream) {
var audioStream = Media.AAC.AudioNormal;
var test = createEmeTest(
testId,
`WidevineLicenseDelay${videoStream.codec}Video`,
'Widevine',
true,
[videoStream, audioStream]);
test.prototype.title =
'Test if we can play video encrypted with Widevine ' +
'encryption with no clear start and 5 seconds license delay.';
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
setupMse(video, runner, videoStream, audioStream);
setupEme(
runner, testEmeHandler, video, videoStream, LicenseManager.WIDEVINE);
video.addEventListener('timeupdate', function onTimeUpdate(e) {
if (!video.paused && video.currentTime >= 15 &&
!testEmeHandler.keyUnusable) {
video.removeEventListener('timeupdate', onTimeUpdate);
runner.checkGE(video.currentTime, 15, 'currentTime');
runner.succeed();
}
});
video.play();
};
};
createWidevineLicenseDelayTest('3.1.7.1', Media.H264.VideoStreamYTCenc);
createWidevineLicenseDelayTest('3.1.8.1', Media.VP9.VideoHighSubSampleEnc);
/**
* Ensure setServerCertificate() is implemented properly.
*/
var createSetServerCertificateTest = function(
testId, testName, assertion, certificateSrc) {
var videoStream = Media.VP9.DrmL3NoHDCP360p30fpsEnc;
var audioStream = Media.AAC.AudioNormal;
var test = createEmeTest(
testId,
testName,
'Widevine',
true,
[videoStream, audioStream]);
test.prototype.title = 'Test support for setServerCertificate';
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
setupMse(video, runner, videoStream, audioStream);
setupEme(
runner, testEmeHandler, video, videoStream, LicenseManager.WIDEVINE);
testEmeHandler.setCertificateSrc(certificateSrc);
video.addEventListener('timeupdate', function onTimeUpdate(e) {
if (!video.paused && video.currentTime >= 5) {
video.removeEventListener('timeupdate', onTimeUpdate);
assertion(runner, testEmeHandler);
runner.succeed();
}
});
video.play();
};
};
createSetServerCertificateTest(
'3.1.9.1', 'CertificateRequestByClient', function(runner, emeHandler) {
runner.assert(
emeHandler.serverCertificateRequested,
'No certificate request generated by client')
runner.assert(emeHandler.messageEncrypted, 'Message is not encrypted');
});
createSetServerCertificateTest(
'3.1.10.1', 'setServerCertificate', function(runner, emeHandler) {
runner.assert(
!emeHandler.setServerCertificateResult,
emeHandler.setServerCertificateResult);
runner.assert(emeHandler.messageEncrypted, 'Message is not encrypted');
}, util.getCertificatePath('valid_widevine_cert.bin'));
createSetServerCertificateTest(
'3.1.11.1',
'setServerCertificateWithInvalidCert', function(runner, emeHandler) {
runner.assert(
emeHandler.setServerCertificateResult,
'setServerCertificate() succeeded with invalid certificate');
}, util.getCertificatePath('invalid_widevine_cert.bin'));
createEncryptedCodecTest(
'3.2.1.1', Media.H264.VideoSmallCenc, Media.AAC.AudioNormal,
LicenseManager.PLAYREADY, false);
createEncryptedCodecTest(
'3.2.2.1', Media.AAC.AudioSmallCenc, Media.H264.VideoNormal,
LicenseManager.PLAYREADY, false);
/**
* Test encrypted event data contains all expected pssh atoms in the initData
* and a null keySystem.
*/
var createEncryptedEventDataTest = function() {
var videoStream = Media.H264.VideoSmallCenc;
var test = createEmeTest(
'3.3.1.1', 'EncryptedEventData', 'General', true, [videoStream]);
test.prototype.title =
'Test encrypted event data contains all expected pssh atoms in the ' +
'initData and a null keySystem.';
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
try {
setupMse(video, runner, videoStream, null);
testEmeHandler.addEventSpies({
onEncrypted: function(e) {
var initData = new Uint8Array(e.initData);
runner.checkEq(initData.length, 856, 'Length of initData');
runner.checkEq(countPsshAtoms(initData), 3, 'Number of pssh atoms');
runner.succeed();
}
});
setupEme(
runner, testEmeHandler, video, videoStream, LicenseManager.WIDEVINE);
} catch (err) {
runner.fail(err);
}
video.play();
};
};
createEncryptedEventDataTest();
/**
* Validate AudioContext#createMediaElementSource succeeds and sends audio
* data for specified mime type.
*/
var createWidevineCreateMESTest = function(testId, encStream, otherStream) {
var videoStream = encStream.mediatype == 'video' ? encStream : otherStream;
var audioStream = encStream.mediatype == 'audio' ? encStream : otherStream;
var test = createEmeTest(
testId,
'Widevine' + encStream.codec + util.MakeCapitalName(encStream.mediatype) +
'CreateMediaElementSource',
'Web Audio API (Optional)',
false,
[videoStream, audioStream]);
test.prototype.title = 'Test if AudioContext#createMediaElementSource ' +
'succeeds and sends audio data for ' + encStream.codec;
test.prototype.start = function(runner, video) {
var testEmeHandler = this.emeHandler;
setupMse(video, runner, videoStream, audioStream);
setupEme(runner, testEmeHandler, video, encStream, LicenseManager.WIDEVINE);
var Ctor = window.AudioContext || window.webkitAudioContext;
var ctx = new Ctor();
video.addEventListener('timeupdate', function onTimeUpdate() {
if (!video.paused && video.currentTime >= 5 &&
!testEmeHandler.keyUnusable) {
video.removeEventListener('timeupdate', onTimeUpdate);
try {
runner.log('Creating MES');
var source = ctx.createMediaElementSource(video);
} catch (e) {
runner.fail(e);
} finally {
ctx.close();
}
runner.checkNE(source, null, 'MediaElementSource');
runner.succeed();
}
});
video.play();
};
}
createWidevineCreateMESTest(
'3.4.1.1', Media.H264.VideoSmallCenc, Media.AAC.AudioNormal);
createWidevineCreateMESTest(
'3.4.2.1', Media.AAC.AudioSmallCenc, Media.H264.VideoNormal);
createWidevineCreateMESTest(
'3.4.3.1', Media.Opus.SintelEncrypted, Media.VP9.VideoNormal);
createWidevineCreateMESTest(
'3.4.4.1', Media.VP9.VideoHighEnc, Media.Opus.CarMed);
return {tests: tests, info: info, fields: fields, viewType: 'default'};
};
try {
exports.getTest = EncryptedmediaTest;
} catch (e) {
// do nothing, this function is not supposed to work for browser, but it's for
// Node js to generate json file instead.
}