chromium/third_party/blink/web_tests/http/tests/media/media-source/mediasource-append-buffer.html

<!DOCTYPE html>
<html>
    <head>
        <script src="/w3c/resources/testharness.js"></script>
        <script src="/w3c/resources/testharnessreport.js"></script>
        <script src="mediasource-util.js"></script>
        <link rel='stylesheet' href='/w3c/resources/testharness.css'>
    </head>
    <body>
        <div id="log"></div>
        <script>
          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                 assert_false(sourceBuffer.updating, 'updating attribute is false');
                 test.done();
              });
          }, 'Test SourceBuffer.appendBuffer() event dispatching.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              assert_throws_dom('InvalidStateError',
                  function() { sourceBuffer.appendBuffer(mediaData); },
                  'appendBuffer() throws an exception there is a pending append.');

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test SourceBuffer.appendBuffer() call during a pending appendBuffer().');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'abort', 'Append aborted.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              sourceBuffer.abort();

              assert_false(sourceBuffer.updating, 'updating attribute is false');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test SourceBuffer.abort() call during a pending appendBuffer().');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);
              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');

                  test.expectEvent(mediaSource, 'sourceended', 'MediaSource sourceended event');
                  mediaSource.endOfStream();
                  assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is "ended"');
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is "ended"');

                  test.expectEvent(mediaSource, 'sourceopen', 'MediaSource sourceopen event');
                  test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
                  test.expectEvent(sourceBuffer, 'update', 'Append success.');
                  test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
                  sourceBuffer.appendBuffer(mediaData);

                  assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is "open"');
                  assert_true(sourceBuffer.updating, 'updating attribute is true');
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is "open"');
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test SourceBuffer.appendBuffer() triggering an "ended" to "open" transition.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);
              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');

                  test.expectEvent(mediaSource, 'sourceended', 'MediaSource sourceended event');
                  mediaSource.endOfStream();
                  assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is "ended"');
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is "ended"');

                  test.expectEvent(mediaSource, 'sourceopen', 'MediaSource sourceopen event');
                  test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
                  test.expectEvent(sourceBuffer, 'update', 'Append success.');
                  test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
                  sourceBuffer.appendBuffer(new Uint8Array(0));

                  assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is "open"');
                  assert_true(sourceBuffer.updating, 'updating attribute is true');
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is "open"');
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test zero byte SourceBuffer.appendBuffer() call triggering an "ended" to "open" transition.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'abort', 'Append aborted.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');
              assert_equals(mediaSource.activeSourceBuffers.length, 0, 'activeSourceBuffers.length');

              test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'sourceBuffers');
              mediaSource.removeSourceBuffer(sourceBuffer);

              assert_false(sourceBuffer.updating, 'updating attribute is false');

              assert_throws_dom('InvalidStateError',
                  function() { sourceBuffer.appendBuffer(mediaData); },
                  'appendBuffer() throws an exception because it isn\'t attached to the mediaSource anymore.');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              assert_throws_dom('InvalidStateError',
                  function() { mediaSource.duration = 1.0; },
                  'set duration throws an exception when updating attribute is true.');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test set MediaSource.duration during a pending appendBuffer() for one of its SourceBuffers.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.failOnEvent(mediaSource, 'sourceended');

              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              assert_throws_dom('InvalidStateError',
                  function() { mediaSource.endOfStream(); },
                  'endOfStream() throws an exception when updating attribute is true.');

              assert_equals(mediaSource.readyState, 'open');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  assert_equals(mediaSource.readyState, 'open');
                  test.done();
              });
          }, 'Test MediaSource.endOfStream() during a pending appendBuffer() for one of its SourceBuffers.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(mediaData);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              assert_throws_dom('InvalidStateError',
                  function() { sourceBuffer.timestampOffset = 10.0; },
                  'set timestampOffset throws an exception when updating attribute is true.');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test set SourceBuffer.timestampOffset during a pending appendBuffer().');

          mediasource_test(function(test, mediaElement, mediaSource)
          {
              var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);

              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(new Uint8Array(0));

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test appending an empty ArrayBufferView.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');

              var arrayBufferView = new Uint8Array(mediaData);

              assert_equals(arrayBufferView.length, mediaData.length, 'arrayBufferView.length before transfer.');

              // Send the buffer as in a message so it gets neutered.
              window.postMessage( 'test', '*', [arrayBufferView.buffer]);

              assert_equals(arrayBufferView.length, 0, 'arrayBufferView.length after transfer.');

              sourceBuffer.appendBuffer(arrayBufferView);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                 assert_false(sourceBuffer.updating, 'updating attribute is false');
                 test.done();
              });
          }, 'Test appending a neutered ArrayBufferView.');

          mediasource_test(function(test, mediaElement, mediaSource)
          {
              var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);

              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendBuffer(new ArrayBuffer(0));

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  test.done();
              });
          }, 'Test appending an empty ArrayBuffer.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');

              var arrayBuffer = mediaData.buffer.slice();

              assert_equals(arrayBuffer.byteLength, mediaData.buffer.byteLength, 'arrayBuffer.byteLength before transfer.');

              // Send the buffer as in a message so it gets neutered.
              window.postMessage( 'test', '*', [arrayBuffer]);

              assert_equals(arrayBuffer.byteLength, 0, 'arrayBuffer.byteLength after transfer.');

              sourceBuffer.appendBuffer(arrayBuffer);

              assert_true(sourceBuffer.updating, 'updating attribute is true');

              test.waitForExpectedEvents(function()
              {
                 assert_false(sourceBuffer.updating, 'updating attribute is false');
                 test.done();
              });
          }, 'Test appending a neutered ArrayBuffer.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
              var halfIndex = (initSegment.length - 1) / 2;
              var partialInitSegment = initSegment.subarray(0, halfIndex);
              var remainingInitSegment = initSegment.subarray(halfIndex);
              var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);

              test.expectEvent(sourceBuffer, 'updateend', 'partialInitSegment append ended.');
              sourceBuffer.appendBuffer(partialInitSegment);

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaElement.readyState, mediaElement.HAVE_NOTHING);
                  assert_equals(mediaSource.duration, Number.NaN);
                  test.expectEvent(sourceBuffer, 'updateend', 'remainingInitSegment append ended.');
                  test.expectEvent(mediaElement, 'loadedmetadata', 'loadedmetadata event received.');
                  sourceBuffer.appendBuffer(remainingInitSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA);
                  assert_equals(mediaSource.duration, segmentInfo.durationInInitSegment);
                  test.expectEvent(sourceBuffer, 'updateend', 'mediaSegment append ended.');
                  test.expectEvent(mediaElement, 'loadeddata', 'loadeddata fired.');
                  sourceBuffer.appendBuffer(mediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA);
                  assert_equals(sourceBuffer.updating, false);
                  assert_equals(mediaSource.readyState, 'open');
                  test.done();
              });
          }, 'Test appendBuffer with partial init segments.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
              var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
              var halfIndex = (mediaSegment.length - 1) / 2;
              var partialMediaSegment = mediaSegment.subarray(0, halfIndex);
              var remainingMediaSegment = mediaSegment.subarray(halfIndex);

              test.expectEvent(sourceBuffer, 'updateend', 'InitSegment append ended.');
              test.expectEvent(mediaElement, 'loadedmetadata', 'loadedmetadata done.');
              sourceBuffer.appendBuffer(initSegment);

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA);
                  assert_equals(mediaSource.duration, segmentInfo.durationInInitSegment);
                  test.expectEvent(sourceBuffer, 'updateend', 'partial media segment append ended.');
                  sourceBuffer.appendBuffer(partialMediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  test.expectEvent(sourceBuffer, 'updateend', 'mediaSegment append ended.');
                  test.expectEvent(mediaElement, 'loadeddata', 'loadeddata fired.');
                  sourceBuffer.appendBuffer(remainingMediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA);
                  assert_equals(mediaSource.readyState, 'open');
                  assert_equals(sourceBuffer.updating, false);
                  test.done();
              });
          }, 'Test appendBuffer with partial media segments.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
              var partialInitSegment = initSegment.subarray(0, initSegment.length / 2);
              var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);

              test.expectEvent(sourceBuffer, 'updateend', 'partialInitSegment append ended.');
              sourceBuffer.appendBuffer(partialInitSegment);

              test.waitForExpectedEvents(function()
              {
                  // Call abort to reset the parser.
                  sourceBuffer.abort();

                  // Append the full intiialization segment.
                  test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
                  sourceBuffer.appendBuffer(initSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  test.expectEvent(sourceBuffer, 'updateend', 'mediaSegment append ended.');
                  test.expectEvent(mediaElement, 'loadeddata', 'loadeddata fired.');
                  sourceBuffer.appendBuffer(mediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  test.done();
              });
          }, 'Test abort in the middle of an initialization segment.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
              var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
              var partialMediaSegment = mediaSegment.subarray(0, 3 * mediaSegment.length / 4);
              var partialBufferedRanges = null;

              test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
              sourceBuffer.appendBuffer(initSegment);

              test.waitForExpectedEvents(function()
              {
                  test.expectEvent(sourceBuffer, 'updateend', 'partialMediaSegment append ended.');
                  sourceBuffer.appendBuffer(partialMediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  // Call abort to reset the parser.
                  sourceBuffer.abort();

                  // Keep a copy of the buffered ranges before we append the whole media segment.
                  partialBufferedRanges = sourceBuffer.buffered;
                  assert_equals(partialBufferedRanges.length, 1, 'partialBufferedRanges has 1 range');

                  // Append the full media segment.
                  test.expectEvent(sourceBuffer, 'updateend', 'mediaSegment append ended.');
                  sourceBuffer.appendBuffer(mediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');

                  assert_equals(sourceBuffer.buffered.length, 1, 'sourceBuffer.buffered has 1 range');
                  assert_equals(sourceBuffer.buffered.length, partialBufferedRanges.length, 'sourceBuffer.buffered and partialBufferedRanges are the same length.');
                  assert_equals(sourceBuffer.buffered.start(0), partialBufferedRanges.start(0), 'sourceBuffer.buffered and partialBufferedRanges start times match.');
                  assert_greater_than(sourceBuffer.buffered.end(0), partialBufferedRanges.end(0), 'sourceBuffer.buffered has a higher end time than partialBufferedRanges.');
                  test.done();
              });
          }, 'Test abort in the middle of a media segment.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
              mediaSource.removeSourceBuffer(sourceBuffer);
              test.waitForExpectedEvents(function()
              {
                  assert_throws_dom('InvalidStateError',
                      function() { sourceBuffer.abort(); },
                      'sourceBuffer.abort() throws an exception for InvalidStateError.');

                  test.done();
              });
          }, 'Test abort after removing sourcebuffer.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
              var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);

              test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
              sourceBuffer.appendBuffer(initSegment);

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'open', 'readyState is open after init segment appended.');
                  test.expectEvent(sourceBuffer, 'updateend', 'mediaSegment append ended.');
                  sourceBuffer.appendBuffer(mediaSegment);
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(sourceBuffer.buffered.length, 1, 'sourceBuffer has a buffered range');
                  assert_equals(mediaSource.readyState, 'open', 'readyState is open after media segment appended.');
                  test.expectEvent(mediaSource, 'sourceended', 'source ended');
                  mediaSource.endOfStream();
              });

              test.waitForExpectedEvents(function()
              {
                  assert_equals(mediaSource.readyState, 'ended', 'readyState is ended.');
                  assert_throws_dom('InvalidStateError',
                      function() { sourceBuffer.abort(); },
                      'sourceBuffer.abort() throws an exception for InvalidStateError.');
                  test.done();
              });

          }, 'Test abort after readyState is ended following init segment and media segment.');

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
              sourceBuffer.appendWindowStart = 1;
              sourceBuffer.appendWindowEnd = 100;
              sourceBuffer.appendBuffer(mediaData);

              test.waitForExpectedEvents(function()
              {
                  assert_false(sourceBuffer.updating, 'updating attribute is false');
                  sourceBuffer.abort();
                  assert_equals(sourceBuffer.appendWindowStart, 0, 'appendWindowStart is reset to 0');
                  assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY, 
                      'appendWindowEnd is reset to +INFINITY');
                  test.done();
              });
          }, 'Test abort after appendBuffer update ends.');

          mediasource_test(function(test, mediaElement, mediaSource)
          {
              var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);

              test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
              test.expectEvent(sourceBuffer, 'update', 'Append success.');
              test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');

              assert_throws_js( TypeError,
                  function() { sourceBuffer.appendBuffer(null); },
                  'appendBuffer(null) throws an exception.');
              test.done();
          }, 'Test appending null.');

          if (window.SharedArrayBuffer) {
              mediasource_test(function(test, mediaElement, mediaSource)
              {
                  var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);

                  test.expectEvent(sourceBuffer, 'updatestart', 'Append started.');
                  test.expectEvent(sourceBuffer, 'update', 'Append success.');
                  test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');

                  assert_throws_js( TypeError,
                      function() { sourceBuffer.appendBuffer(new Uint8Array(new SharedArrayBuffer(16))); },
                      'appendBuffer() of SharedArrayBuffer view throws an exception.');
                  test.done();
              }, 'Test appending SharedArrayBuffer view.');
          }

          mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
          {
              mediaSource.removeSourceBuffer(sourceBuffer);

              assert_throws_dom( 'InvalidStateError',
                  function() { sourceBuffer.appendBuffer(mediaData); },
                  'appendBuffer() throws an exception when called after removeSourceBuffer().');
              test.done();
          }, 'Test appending after removeSourceBuffer().');
        </script>
    </body>
</html>