| 
 | 1 | +<!DOCTYPE html>  | 
 | 2 | +<html>  | 
 | 3 | +  <title>Test MediaSource addSourceBuffer overloads for WebCodecs Audio/VideoDecoderConfigs</title>  | 
 | 4 | +<script src="/resources/testharness.js"></script>  | 
 | 5 | +<script src="/resources/testharnessreport.js"></script>  | 
 | 6 | +<script>  | 
 | 7 | + | 
 | 8 | +setup(() => {  | 
 | 9 | +  assert_implements(  | 
 | 10 | +      SourceBuffer.prototype.hasOwnProperty('appendEncodedChunks'),  | 
 | 11 | +      'SourceBuffer prototype hasOwnProperty "appendEncodedChunks", used ' +  | 
 | 12 | +          'here to feature detect MSE-for-WebCodecs implementation.');  | 
 | 13 | +});  | 
 | 14 | + | 
 | 15 | +testInvalidArguments();  | 
 | 16 | +testValidArguments();  | 
 | 17 | + | 
 | 18 | +function getValidAudioConfig() {  | 
 | 19 | +  // TODO(crbug.com/1144908): Consider confirming with WebCodecs'  | 
 | 20 | +  // isConfigSupported() once that API is available.  | 
 | 21 | +  return {  | 
 | 22 | +    codec: 'opus',  | 
 | 23 | +    sampleRate: 48000,  | 
 | 24 | +    numberOfChannels: 2  | 
 | 25 | +  };  | 
 | 26 | +}  | 
 | 27 | + | 
 | 28 | +function getValidVideoConfig() {  | 
 | 29 | +  // TODO(crbug.com/1144908): Consider confirming with WebCodecs'  | 
 | 30 | +  // isConfigSupported() once that API is available.  | 
 | 31 | +  return { codec: 'vp09.00.10.08' };  | 
 | 32 | +}  | 
 | 33 | + | 
 | 34 | +function testInvalidArguments() {  | 
 | 35 | +  const INVALID_CASES = [  | 
 | 36 | +    { arg: null,  | 
 | 37 | +      name: 'null' },  | 
 | 38 | +    { arg: undefined,  | 
 | 39 | +      name: 'undefined' },  | 
 | 40 | +    { arg: { },  | 
 | 41 | +      name: '{ empty dictionary }' },  | 
 | 42 | +    {  | 
 | 43 | +      arg: {  | 
 | 44 | +        audioConfig: getValidAudioConfig(),  | 
 | 45 | +        videoConfig: getValidVideoConfig()  | 
 | 46 | +      },  | 
 | 47 | +      name: '{ valid audioConfig and videoConfig }',  | 
 | 48 | +    },  | 
 | 49 | +    {  | 
 | 50 | +      arg: {  | 
 | 51 | +        audioConfig: {  | 
 | 52 | +          codec: 'bogus',  | 
 | 53 | +          sampleRate: 48000,  | 
 | 54 | +          numberOfChannels: 2  | 
 | 55 | +        }  | 
 | 56 | +      },  | 
 | 57 | +      name: 'bad audio config codec',  | 
 | 58 | +    },  | 
 | 59 | +    { arg: { videoConfig: { codec: 'bogus' } },  | 
 | 60 | +      name: 'bad video config codec' },  | 
 | 61 | +    { arg: { audioConfig: { sampleRate: 48000, numberOfChannels: 2 } },  | 
 | 62 | +      name: 'audio config missing required member "codec"' },  | 
 | 63 | +    { arg: { videoConfig: { } },  | 
 | 64 | +      name: 'video config missing required member "codec"' },  | 
 | 65 | +  ];  | 
 | 66 | + | 
 | 67 | +  [ 'closed', 'open', 'ended' ].forEach(readyStateScenario => {  | 
 | 68 | +    INVALID_CASES.forEach(invalidCase => {  | 
 | 69 | +      runAddSourceBufferTest(invalidCase['arg'] /* argument */,  | 
 | 70 | +          false /* isValidArgument */,  | 
 | 71 | +          invalidCase['name'] /* argumentDescription */,  | 
 | 72 | +          readyStateScenario);  | 
 | 73 | +    });  | 
 | 74 | +  });  | 
 | 75 | +}  | 
 | 76 | + | 
 | 77 | +function testValidArguments() {  | 
 | 78 | +  const VALID_CASES = [  | 
 | 79 | +    {  | 
 | 80 | +      arg: {  | 
 | 81 | +        audioConfig: getValidAudioConfig()  | 
 | 82 | +      },  | 
 | 83 | +      name: 'valid audioConfig'  | 
 | 84 | +    },  | 
 | 85 | +    {  | 
 | 86 | +      arg: {  | 
 | 87 | +        videoConfig: getValidVideoConfig()  | 
 | 88 | +      },  | 
 | 89 | +      name: 'valid videoConfig'  | 
 | 90 | +    },  | 
 | 91 | +  ];  | 
 | 92 | + | 
 | 93 | +  [ 'closed', 'open', 'ended' ].forEach(readyStateScenario => {  | 
 | 94 | +    VALID_CASES.forEach(validCase => {  | 
 | 95 | +      runAddSourceBufferTest(  | 
 | 96 | +          validCase['arg'] /* argument */,  | 
 | 97 | +          true /* isValidArgument */,  | 
 | 98 | +          validCase['name'] /* argumentDescription */,  | 
 | 99 | +          readyStateScenario);  | 
 | 100 | +    });  | 
 | 101 | +  });  | 
 | 102 | +}  | 
 | 103 | + | 
 | 104 | +async function getClosedMediaSource(test) {  | 
 | 105 | +  let mediaSource = new MediaSource();  | 
 | 106 | +  assert_equals(mediaSource.readyState, 'closed');  | 
 | 107 | +  return mediaSource;  | 
 | 108 | +}  | 
 | 109 | + | 
 | 110 | +async function getOpenMediaSource(test) {  | 
 | 111 | +  return new Promise(async resolve => {  | 
 | 112 | +    const v = document.createElement('video');  | 
 | 113 | +    const mediaSource = new MediaSource();  | 
 | 114 | +    const url = URL.createObjectURL(mediaSource);  | 
 | 115 | +    mediaSource.addEventListener('sourceopen', test.step_func(() => {  | 
 | 116 | +      URL.revokeObjectURL(url);  | 
 | 117 | +      assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');  | 
 | 118 | +      resolve(mediaSource);  | 
 | 119 | +    }), { once: true });  | 
 | 120 | +    v.src = url;  | 
 | 121 | +  });  | 
 | 122 | +}  | 
 | 123 | + | 
 | 124 | +async function getEndedMediaSource(test) {  | 
 | 125 | +  let mediaSource = await getOpenMediaSource(test);  | 
 | 126 | +  assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');  | 
 | 127 | +  mediaSource.endOfStream();  | 
 | 128 | +  assert_equals(mediaSource.readyState, 'ended', 'MediaSource is ended');  | 
 | 129 | +  return mediaSource;  | 
 | 130 | +}  | 
 | 131 | + | 
 | 132 | +function runAddSourceBufferTest(argument, isValidArgument, argumentDescription, readyStateScenario) {  | 
 | 133 | +  const testDescription = 'addSourceBuffer call with ' +  | 
 | 134 | +      (isValidArgument ? 'valid' : 'invalid') +  | 
 | 135 | +      ' argument ' + argumentDescription + ' while MediaSource readyState is ' +  | 
 | 136 | +      readyStateScenario;  | 
 | 137 | + | 
 | 138 | +  switch(readyStateScenario) {  | 
 | 139 | +    case 'closed':  | 
 | 140 | +      promise_test(async t => {  | 
 | 141 | +        let mediaSource = await getClosedMediaSource(t);  | 
 | 142 | +        assert_equals(mediaSource.readyState, 'closed');  | 
 | 143 | +        let sourceBuffer;  | 
 | 144 | +        if (isValidArgument) {  | 
 | 145 | +          assert_throws_dom('InvalidStateError',  | 
 | 146 | +              () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 147 | +              'addSourceBuffer(valid config) throws InvalidStateError if MediaSource is "closed"');  | 
 | 148 | +          assert_equals(sourceBuffer, undefined,  | 
 | 149 | +              'addSourceBuffer result for valid config while "closed" should be exception');  | 
 | 150 | +        } else {  | 
 | 151 | +          assert_throws_js(TypeError,  | 
 | 152 | +              () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 153 | +              'addSourceBuffer(invalid config) throws TypeError if MediaSource is "closed"');  | 
 | 154 | +          assert_equals(sourceBuffer, undefined,  | 
 | 155 | +              'addSourceBuffer result for invalid config while "closed" should be exception');  | 
 | 156 | +        }  | 
 | 157 | +      }, testDescription);  | 
 | 158 | +      break;  | 
 | 159 | +    case 'open':  | 
 | 160 | +      promise_test(async t => {  | 
 | 161 | +        let mediaSource = await getOpenMediaSource(t);  | 
 | 162 | +        assert_equals(mediaSource.readyState, 'open', 'MediaSource is open');  | 
 | 163 | +        let sourceBuffer;  | 
 | 164 | +        if (isValidArgument) {  | 
 | 165 | +          // TODO(crbug.com/1144908): Update to expect success once the impl is  | 
 | 166 | +          // more complete.  | 
 | 167 | +          assert_throws_dom('QuotaExceededError',  | 
 | 168 | +            () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 169 | +            'addSourceBuffer(valid config) throws QuotaExceededError');  | 
 | 170 | +          assert_equals(sourceBuffer, undefined,  | 
 | 171 | +            'addSourceBuffer result for valid config while "open" should be exception');  | 
 | 172 | +        } else {  | 
 | 173 | +          assert_throws_js(TypeError,  | 
 | 174 | +            () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 175 | +            'addSourceBuffer(invalid config) throws TypeError if MediaSource is "open"');  | 
 | 176 | +          assert_equals(sourceBuffer, undefined,  | 
 | 177 | +            'addSourceBufferResult for invalid config while "open" should be exception');  | 
 | 178 | +        }  | 
 | 179 | +      }, testDescription);  | 
 | 180 | +      break;  | 
 | 181 | +    case 'ended':  | 
 | 182 | +      promise_test(async t => {  | 
 | 183 | +        let mediaSource = await getEndedMediaSource(t);  | 
 | 184 | +        let sourceBuffer;  | 
 | 185 | +        if (isValidArgument) {  | 
 | 186 | +          assert_throws_dom('InvalidStateError',  | 
 | 187 | +              () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 188 | +              'addSourceBuffer(valid config) throws InvalidStateError if MediaSource is "ended"');  | 
 | 189 | +          assert_equals(sourceBuffer, undefined,  | 
 | 190 | +              'addSourceBuffer result for valid config while "ended" should be exception');  | 
 | 191 | +        } else {  | 
 | 192 | +          assert_throws_js(TypeError,  | 
 | 193 | +              () => { sourceBuffer = mediaSource.addSourceBuffer(argument); },  | 
 | 194 | +              'addSourceBuffer(invalid config) throws TypeError if MediaSource is "ended"');  | 
 | 195 | +          assert_equals(sourceBuffer, undefined,  | 
 | 196 | +              'addSourceBuffer result for invalid config while "ended" should be exception');  | 
 | 197 | +        }  | 
 | 198 | +      }, testDescription);  | 
 | 199 | +      break;  | 
 | 200 | +    default:  | 
 | 201 | +      assert_unreached('Invalid readyStateScenario ' + readyStateScenario);  | 
 | 202 | +      break;  | 
 | 203 | +  }  | 
 | 204 | +}  | 
 | 205 | + | 
 | 206 | +</script>  | 
 | 207 | +</html>  | 
0 commit comments