2020/functional/formatSupportTest.js (426 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'; /** * Format Support Test Suite. * @class */ var FormatsupportTest = function() { var tests = []; var createFunctionalTest = function(testId, name, category = 'Functional', mandatory = true) { var t = createTest(name, category, mandatory, testId, 'Format Support Tests'); t.prototype.index = tests.length; tests.push(t); return t; }; /** * Test if specified type is supported through MediaSource.isTypeSupported. */ var createSupportTest = function(testId, name, evaluate, mandatory) { var logResult = function(format, result) { this.runner.log('[' + this.desc + '] - isTypeSupported("' + format + '") returned: ' + result); }; var test = createFunctionalTest(testId, name, 'Support', mandatory); test.prototype.title = 'Test ' + name; test.prototype.start = function() { try { if (evaluate(logResult.bind(this), this.runner)) this.runner.succeed(); else throw name + ' failed.'; } catch (e) { this.runner.fail(e); } } }; var isTypeSupportedTest = function(logResult) { var baselineVideoType = util.createVideoFormatStr( 'mp4', 'avc1.4d401e', 640, 360, 30, null, 'bitrate=300000'); var baselineVideoSupported = MediaSource.isTypeSupported(baselineVideoType); logResult(baselineVideoType, baselineVideoSupported); var baselineAudioType = util.createAudioFormatStr('mp4', 'mp4a.40.2', 'channels=2'); var baselineAudioSupported = MediaSource.isTypeSupported(baselineAudioType); logResult(baselineAudioType, baselineAudioSupported); var invalidVideoHeightType = util.createVideoFormatStr( 'mp4', 'avc1.4d401e', 640, 10360); var invalidVideoHeightSupported = MediaSource.isTypeSupported(invalidVideoHeightType); logResult(invalidVideoHeightType, invalidVideoHeightSupported); var invalidAudioChannelsType = util.createAudioFormatStr( 'mp4', 'mp4a.40.2', 'channels=100'); var invalidAudioChannelsSupported = MediaSource.isTypeSupported(invalidAudioChannelsType); logResult(invalidAudioChannelsType, invalidAudioChannelsSupported); if (baselineVideoSupported && baselineAudioSupported && !invalidVideoHeightSupported && !invalidAudioChannelsSupported) return true; }; createSupportTest( '20.1.1.1', 'isTypeSupported cryptoblockformat', function(logResult) { var invalidCryptoBlockFormatType = util.createVideoFormatStr( 'webm', 'vp9', 1280, 720, 23.976, null, 'cryptoblockformat=invalid'); var invalidCryptoBlockFormatSupported = MediaSource.isTypeSupported(invalidCryptoBlockFormatType); logResult( invalidCryptoBlockFormatType, invalidCryptoBlockFormatSupported); var validCryptoBlockFormatType = util.createVideoFormatStr( 'webm', 'vp9', 1280, 720, 23.976, null, 'cryptoblockformat=subsample'); var validCryptoBlockFormatSupported = MediaSource.isTypeSupported(validCryptoBlockFormatType); logResult(validCryptoBlockFormatType, validCryptoBlockFormatSupported); if (!invalidCryptoBlockFormatSupported && validCryptoBlockFormatSupported) { return true; } }); createSupportTest( '20.1.2.1', 'isTypeSupported Extensions', isTypeSupportedTest); createSupportTest( '20.1.3.1', 'isTypeSupported AV1 Codec', function(logResult, runner) { runner.checkEq( false, MediaSource.isTypeSupported('video/mp4; codecs="av1"')); runner.checkEq( false, MediaSource.isTypeSupported('video/mp4; codecs="av00.0.00M.00"')); runner.checkEq( true, MediaSource.isTypeSupported( util.createVideoFormatStr('mp4', util.av1Codec()))); return true; }, util.requireAV1()); var createHfrSupportTest = function(testId, format, container, codec, fps, mandatory) { createSupportTest( testId, `${format} ${fps}fps Support`, function(logResult) { if (!(isTypeSupportedTest(logResult))) return false; var maxSupportedWindow = (format => { if (format == 'H.264') return util.getMaxH264SupportedWindow(); if (format == 'VP9') return util.getMaxVp9SupportedWindow(); if (format == 'AV1') return util.getMaxAv1SupportedWindow(); throw `Invalid format: ${format}`; })(format); var maxWidth = maxSupportedWindow[0]; var maxHeight = maxSupportedWindow[1]; var testTypeSupported = (width, height, fps) => { var type = util.createVideoFormatStr(container, codec, width, height, fps); var supported = MediaSource.isTypeSupported(type); logResult(type, supported); return supported; }; var invalidTypeSupported = testTypeSupported(640, 360, 9999); var baselineTypeSupported = testTypeSupported(640, 360, fps); var maxResolutionTypeSupported = testTypeSupported(maxWidth, maxHeight, fps); return baselineTypeSupported && maxResolutionTypeSupported && !invalidTypeSupported; }, mandatory); }; createHfrSupportTest('20.1.4.1', 'H.264', 'mp4', 'avc1.4d401e', 60); createHfrSupportTest('20.1.5.1', 'VP9', 'webm', 'vp9', 60); createHfrSupportTest('20.1.6.1', 'AV1', 'mp4', util.av1Codec(), 60, false); createHfrSupportTest('20.1.7.1', 'H.264', 'mp4', 'avc1.4d401e', 120, false); createHfrSupportTest('20.1.8.1', 'VP9', 'webm', 'vp9', 120, false); createHfrSupportTest('20.1.9.1', 'AV1', 'mp4', util.av1Codec(), 120, false); createSupportTest( '20.1.10.1', 'isTypeSupported EOTF Support', function(logResult) { var smpte2084Type = util.createVideoFormatStr( 'webm', 'vp9.2', 1280, 720, 30, null, 'eotf=smpte2084'); var smpte2084Supported = MediaSource.isTypeSupported(smpte2084Type); logResult(smpte2084Type, smpte2084Supported); var bt709Type = util.createVideoFormatStr( 'webm', 'vp9.2', 1280, 720, 30, null, 'eotf=bt709'); var bt709Supported = MediaSource.isTypeSupported(bt709Type); logResult(bt709Type, bt709Supported); var hlgType = util.createVideoFormatStr( 'webm', 'vp9.2', 1280, 720, 30, null, 'eotf=arib-std-b67'); var hlgSupported = MediaSource.isTypeSupported(hlgType); logResult(hlgType, hlgSupported); var invalidEOTFType = util.createVideoFormatStr( 'webm', 'vp9.2', 1280, 720, 30, null, 'eotf=strobevision'); var invalidEOTFSupported = MediaSource.isTypeSupported(invalidEOTFType); logResult(invalidEOTFType, invalidEOTFSupported); if (smpte2084Supported && bt709Supported && hlgSupported && !invalidEOTFSupported) return true; }, harnessConfig.support_hdr); createSupportTest( '20.1.11.1', 'SMPTE2084 Support', function(logResult) { var smpte2084Type = util.createSimpleVideoFormatStr('webm', 'vp9.2', 'eotf=smpte2084'); var smpte2084Supported = MediaSource.isTypeSupported(smpte2084Type); logResult(smpte2084Type, smpte2084Supported); if (smpte2084Supported) return true; }, harnessConfig.support_hdr); createSupportTest( '20.1.12.1', 'ARIB STD-B67 Support', function(logResult) { var hlgType = util.createSimpleVideoFormatStr( 'webm', 'vp9.2', 'eotf=arib-std-b67'); var hlgSupported = MediaSource.isTypeSupported(hlgType); logResult(hlgType, hlgSupported); if (hlgSupported) return true; }, harnessConfig.support_hdr); createSupportTest( '20.1.13.1', 'ITU-R BT.709 Support', function(logResult) { var bt709Type = util.createSimpleVideoFormatStr('webm', 'vp9.2', 'eotf=bt709'); var bt709Supported = MediaSource.isTypeSupported(bt709Type); logResult(bt709Type, bt709Supported); if (bt709Supported) return true; }, harnessConfig.support_hdr); createSupportTest( '20.1.14.1', 'Spherical(decode-to-texture) Support', function(logResult) { if (!(isTypeSupportedTest(logResult))) return false; var invalidSphericalType = util.createSimpleVideoFormatStr( 'webm', 'vp9', 'decode-to-texture=nope'); var invalidSphericalSupported = MediaSource.isTypeSupported(invalidSphericalType); logResult(invalidSphericalType, invalidSphericalSupported); var validSphericalTrueType = util.createSimpleVideoFormatStr( 'webm', 'vp9', 'decode-to-texture=true'); var validSphericalTrueSupported = MediaSource.isTypeSupported(validSphericalTrueType); logResult(validSphericalTrueType, validSphericalTrueSupported); var validSphericalFalseType = util.createSimpleVideoFormatStr( 'webm', 'vp9', 'decode-to-texture=false'); var validSphericalFalseSupported = MediaSource.isTypeSupported(validSphericalFalseType); logResult(validSphericalFalseType, validSphericalFalseSupported); if (validSphericalTrueSupported && validSphericalFalseSupported && !invalidSphericalSupported) { return true; } }, util.isCobalt() && !harnessConfig.novp9); /** * Validate specified video format could be played. */ var createMediaFormatTest = function(testId, name, stream, codec, mandatory) { var test = createFunctionalTest(testId, name, 'Video / Audio', mandatory); test.prototype.title = 'Test if browser can play ' + name; test.prototype.start = function() { try { var videoElement = document.createElement('video'); if (!!(MediaSource.isTypeSupported && MediaSource.isTypeSupported( stream + '; codecs="' + codec + '"'))) { this.runner.succeed(); } else { throw name + ' not supported.'; } } catch (e) { this.runner.fail(e); } }; }; createMediaFormatTest('20.2.1.1', 'MP4 + H.264', 'video/mp4', 'avc1.4d401e'); createMediaFormatTest('20.2.2.1', 'WebM + VP9', 'video/webm', 'vp9'); createMediaFormatTest( '20.2.3.1', 'WebM + VP9 Profile 2', 'video/webm', 'vp9.2'); createMediaFormatTest('20.2.4.1', 'WebM + Opus', 'audio/webm', 'opus', false); createMediaFormatTest('20.2.5.1', 'MP4 + AC3', 'audio/mp4', 'ac-3', false); createMediaFormatTest('20.2.6.1', 'MP4 + EAC3', 'audio/mp4', 'ec-3', false); createMediaFormatTest('20.2.7.1', 'MP4 + AV1 (Level 4.1 SDR)', 'video/mp4', util.av1Codec('4.1'), util.requireAV1()); createMediaFormatTest('20.2.8.1', 'MP4 + AV1 (Level 5.1 SDR)', 'video/mp4', util.av1Codec('5.1'), util.requireAV1()); createMediaFormatTest('20.2.9.1', 'MP4 + AV1 (Level 5.1 HDR)', 'video/mp4', util.av1Codec('5.1', 10), util.requireAV1()); createMediaFormatTest('20.2.10.1', 'MP4 + AV1 (Level 6.0 SDR)', 'video/mp4', util.av1Codec('6.0'), util.isGt4K()); createMediaFormatTest('20.2.11.1', 'MP4 + AV1 (Level 6.0 HDR)', 'video/mp4', util.av1Codec('6.0', 10), util.isGt4K()); /** * Validate if VP9 live format can be played. */ var createLivePlaybackSupportTest = function( testId, videoStream, audioStream, expectedPlayTimeInS, sizeToFetch) { var test = createFunctionalTest( testId, sizeToFetch ? 'PartialSegmentPlayback' : 'Playback', 'VP9 Live'); test.prototype.title = 'Test if playback of VP9 live format is supported.'; test.prototype.start = function(runner, video) { var ms = new MediaSource(); var videoSb; var audioSb; ms.addEventListener('sourceopen', function() { videoSb = ms.addSourceBuffer(videoStream.mimetype); audioSb = ms.addSourceBuffer(audioStream.mimetype); }); video.src = window.URL.createObjectURL(ms); var videoXhr = runner.XHRManager.createRequest( videoStream.src, function(e) { videoSb.appendBuffer(this.getResponseData()); video.addEventListener('timeupdate', function(e) { if (!video.paused && video.currentTime > expectedPlayTimeInS) { runner.succeed(); } }, 0, sizeToFetch); video.play(); }); var audioXhr = runner.XHRManager.createRequest( audioStream.src, function(e) { audioSb.appendBuffer(this.getResponseData()); videoXhr.send(); }, 0, sizeToFetch); audioXhr.send(); }; }; createLivePlaybackSupportTest( '20.3.1.1', Media.VP9.VideoLive, Media.AAC.AudioForVP9Live, 14); createLivePlaybackSupportTest( '20.3.2.1', Media.VP9.VideoLive, Media.AAC.AudioForVP9Live, 3, 80000); /** * Test HDR 10-bit VP9 Profile 2 (HLG and PQ). */ var create10BitVp9Test = function(testId, videoStream, audioStream) { if (!audioStream) { audioStream = Media.AAC.AudioNormal; } var mandatory = util.isGt4K() || (util.isGtFHD() && videoStream.get('fps') <= 30); var test = createFunctionalTest( testId, `VP9.Profile2.10Bit.${videoStream.get('transferFunction')}` + `.${videoStream.get('resolution')}${videoStream.get('fps')}`, 'VP9 HDR', mandatory); test.prototype.title = 'Test playback of HDR 10-bit VP9 Profile 2.'; test.prototype.start = function(runner, video) { if (!isTypeSupported(videoStream)) { runner.fail(`MIME type not supported: '${videoStream.mimetype}'`); return; } setupMse(video, runner, videoStream, audioStream); video.addEventListener('timeupdate', function(e) { if (!video.paused && video.currentTime > 5) { runner.succeed(); } }); video.play(); }; } // Transfer function: HLG create10BitVp9Test('20.4.1.1', Media.VP9.HdrHlgUltralow); create10BitVp9Test('20.4.2.1', Media.VP9.HdrHlgLow); create10BitVp9Test('20.4.3.1', Media.VP9.HdrHlgMed); create10BitVp9Test('20.4.4.1', Media.VP9.HdrHlgHigh); create10BitVp9Test('20.4.5.1', Media.VP9.HdrHlg720p); create10BitVp9Test('20.4.6.1', Media.VP9.HdrHlg1080p); create10BitVp9Test('20.4.7.1', Media.VP9.HdrHlg2k); create10BitVp9Test('20.4.8.1', Media.VP9.HdrHlg4k); create10BitVp9Test('20.4.9.1', Media.VP9.HdrHlgUltralowHfr); create10BitVp9Test('20.4.10.1', Media.VP9.HdrHlgLowHfr); create10BitVp9Test('20.4.11.1', Media.VP9.HdrHlgMedHfr); create10BitVp9Test('20.4.12.1', Media.VP9.HdrHlgHighHfr); create10BitVp9Test('20.4.13.1', Media.VP9.HdrHlg720pHfr); create10BitVp9Test('20.4.14.1', Media.VP9.HdrHlg1080pHfr); create10BitVp9Test('20.4.15.1', Media.VP9.HdrHlg2kHfr); create10BitVp9Test('20.4.16.1', Media.VP9.HdrHlg4kHfr); // Transfer function: PQ create10BitVp9Test('20.4.17.1', Media.VP9.HdrPqUltralow); create10BitVp9Test('20.4.18.1', Media.VP9.HdrPqLow); create10BitVp9Test('20.4.19.1', Media.VP9.HdrPqMed); create10BitVp9Test('20.4.20.1', Media.VP9.HdrPqHigh); create10BitVp9Test('20.4.21.1', Media.VP9.HdrPq720p); create10BitVp9Test('20.4.22.1', Media.VP9.HdrPq1080p); create10BitVp9Test('20.4.23.1', Media.VP9.HdrPq2k); create10BitVp9Test('20.4.24.1', Media.VP9.HdrPq4k); create10BitVp9Test( '20.4.25.1', Media.VP9.HdrPqUltralowHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.26.1', Media.VP9.HdrPqLowHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.27.1', Media.VP9.HdrPqMedHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.28.1', Media.VP9.HdrPqHighHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.29.1', Media.VP9.HdrPq720pHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.30.1', Media.VP9.HdrPq1080pHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.31.1', Media.VP9.HdrPq2kHfr, Media.AAC.AudioMeridian); create10BitVp9Test( '20.4.32.1', Media.VP9.HdrPq4kHfr, Media.AAC.AudioMeridian); /** * Validate specified mimetype is supported. */ var createMimeTypeTest = function(testId, mimetype, desc, mandatory = true) { var test = createFunctionalTest(testId, desc + 'Support', 'MIME Type Support', mandatory); test.prototype.start = function(runner, video) { this.ms = new MediaSource(); this.ms.addEventListener('sourceopen', this.onsourceopen.bind(this)); if (this.ms.isWrapper) this.ms.attachTo(video); else this.video.src = window.URL.createObjectURL(this.ms); }; test.prototype.title = 'Test if we support ' + desc + ' with mimetype: ' + mimetype; test.prototype.onsourceopen = function() { try { this.log('Trying format ' + mimetype); var src = this.ms.addSourceBuffer(mimetype); } catch (e) { return this.runner.fail(e); } this.runner.succeed(); }; }; createMimeTypeTest('20.5.1.1', Media.AAC.mimetype, 'AAC'); createMimeTypeTest('20.5.2.1', Media.H264.mimetype, 'H264'); createMimeTypeTest('20.5.3.1', Media.VP9.mimetype, 'VP9'); createMimeTypeTest('20.5.4.1', Media.Opus.mimetype, 'Opus'); createMimeTypeTest('20.5.5.1', Media.AC3.mimetype, 'AC3', false); createMimeTypeTest('20.5.6.1', Media.EAC3.mimetype, 'EAC3', false); var createAudio51Test = function(testId, audioStream, mandatory) { var test = createFunctionalTest(testId, `${audioStream.codec} 5.1`, 'Media', mandatory); test.prototype.start = function(runner, video) { this.ms = new MediaSource(); this.ms.addEventListener('sourceopen', this.onsourceopen.bind(this)); if (this.ms.isWrapper) this.ms.attachTo(video); else this.video.src = window.URL.createObjectURL(this.ms); }; test.prototype.title = `Test 5.1-channel ${audioStream.codec}`; test.prototype.onsourceopen = function() { var runner = this.runner; var media = this.video; var videoStream = Media.H264.VideoNormal; try { var audioSb = this.ms.addSourceBuffer(audioStream.mimetype); var videoSb = this.ms.addSourceBuffer(videoStream.mimetype); var xhr = runner.XHRManager.createRequest(audioStream.src, function(e) { audioSb.appendBuffer(xhr.getResponseData()); var xhr2 = runner.XHRManager.createRequest(videoStream.src, function(e) { videoSb.appendBuffer(xhr2.getResponseData()); media.play(); media.addEventListener('timeupdate', function(e) { if (!media.paused && media.currentTime > 2) { runner.succeed(); } }); }, 0, 3000000); xhr2.send(); }); } catch (error) { runner.fail(error); } xhr.send(); }; }; /** * Test different audio codecs with 5.1 surround sound (six channel surround * sound audio system). */ createAudio51Test('20.6.3.1', Media.Opus.Audio51); createAudio51Test('20.6.4.1', Media.AAC.Audio51); createAudio51Test('20.6.5.1', Media.AC3.Audio51, false); createAudio51Test('20.6.6.1', Media.EAC3.Audio51, false); return {tests: tests, viewType: 'default'}; }; try { exports.getTest = FormatsupportTest; } catch (e) { // do nothing, this function is not supposed to work for browser, but it's for // Node js to generate json file instead. }