Javascript  |  480行  |  14.01 KB

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
"use strict";

(function() {
  window.__eventReporter = {};
  window.__testState = {};

  var logger = new Logger();
  window.__logger = logger;

  var video_format = 'video/mp4; codecs="avc1.640028"';
  var audio_format = 'audio/mp4; codecs="mp4a.40.2"';

  var audio1MB = createAudioDef(
      'http://localhost:8000/files/car-audio-1MB-trunc.mp4', 1048576, 65.875);
  var video1MB = createVideoDef(
      'http://localhost:8000/files/test-video-1MB.mp4', 1031034, 1.04);

  // Utility functions.
  function Logger() {
    var logs = [];

    this.log = function(log_string) {
      logs.push(log_string);
    };

    this.toString = function() {
      var output = '';
      for (var i in logs)
        output += logs[i];

      return output;
    }
  }

  function createAudioDef(src, size, duration) {
    return {
      type: 'audio',
      format: audio_format,
      size: size,
      src: src,
      duration: duration,
      bps: Math.floor(size / duration)
    };
  }

  function createVideoDef(src, size, duration) {
    return {
      type: 'video',
      format: video_format,
      size: size,
      src: src,
      duration: duration,
      bps: Math.floor(size / duration)
    };
  }

  function createMediaSource() {
    if (typeof MediaSource !== 'undefined')
      return new MediaSource();
    else
      return new WebKitMediaSource();
  }

  function createVideo() {
    return document.createElement('video');
  }

  function setupVideoAndMs(onSourceopen) {
    var temp_video = createVideo();
    var ms = createMediaSource();
    ms.addEventListener('webkitsourceopen', onSourceopen);
    var ms_url = window.URL.createObjectURL(ms);
    temp_video.src = ms_url;
    return {
      'video': temp_video,
      'ms': ms
    };
  }

  function XHRWrapper(file, onLoad, onError, start, length) {
    self = this;

    self.file = file;
    self.onLoad = onLoad;
    self.onError = onError;
    self.start = start;
    self.length = length;

    this.getResponseData = function() {
      var result = new Uint8Array(this.xhr.response);
      if (start != null) {
        return result.subarray(start, start + length);
      }
      return result;
    };

    this.abort = function() {
      this.xhr.abort();
    };

    this.send = function() {
      this.xhr.send();
    };

    this.xhr = new XMLHttpRequest();
    this.xhr.open('GET', file, true);

    this.xhr.addEventListener('load', function(e) {
      self.onLoad(e);
    });

    this.xhr.addEventListener('error', function(e) {
      logger.log('XHR errored.');
      self.onError(e);
    });

    this.xhr.addEventListener('timeout', function(e) {
      logger.log('XHR timed out.');
      self.onError(e);
    });

    this.xhr.responseType = 'arraybuffer';
    if (length != null) {
      start = start || 0;
      this.xhr.setRequestHeader(
        'Range',
        'bytes=' + start + '-' + (start + length - 1)
      );
    }
  }

  function approxEq(a, b) {
    return Math.abs(a - b) < 0.5;
  };

  // MSE tests.
  window.__testAttach = function() {
    var ms = createMediaSource();
    ms.addEventListener('webkitsourceopen', function() {
      window.__eventReporter['sourceopen'] = true;
    });

    var video = document.getElementById('main_player');
    video.src = window.URL.createObjectURL(ms);
    video.load();
  };

  window.__testAddSourceBuffer = function() {
    var vm = setupVideoAndMs(function() {
      try {
        var return_value = true;
        return_value &= vm.ms.sourceBuffers.length === 0;
        vm.ms.addSourceBuffer(audio_format);
        return_value &= vm.ms.sourceBuffers.length === 1;
        vm.ms.addSourceBuffer(video_format);
        return_value &= vm.ms.sourceBuffers.length === 2;

        window.__testState['addSourceBuffer'] = !!return_value;
      }
      catch (e) {
        window.__testState['addSourceBuffer'] = false;
      }
    });
  };

  window.__testAddSupportedFormats = function() {
    var formats = [
      audio_format,
      video_format,
    ];

    var vm = setupVideoAndMs(function() {
      for (var i = 0; i < formats.length; ++i) {
        try {
          vm.ms.addSourceBuffer(formats[i]);
        } catch (e) {
          window.__testState['addSupportedFormats'] = false;
          return;
        }
      }
      window.__testState['addSupportedFormats'] = true;
    });
  };

  window.__testAddSourceBufferException = function() {
    var vm = setupVideoAndMs(function() {
      try {
        vm.ms.addSourceBuffer('^^^');
        window.__testState['addSourceBufferException'] = false;
        return;
      }
      catch (e) {
        if (e.code !== DOMException.NOT_SUPPORTED_ERR) {
          window.__testState['addSourceBufferException'] = false;
          return;
        }
      }

      try {
        var temp_media_source = new WebKitMediaSource();
        temp_media_source.addSourceBuffer(audio_format);
        window.__testState['addSourceBufferException'] = false;
        return;
      }
      catch (e) {
        if (e.code !== DOMException.INVALID_STATE_ERR) {
          window.__testState['addSourceBufferException'] = false;
          return;
        }
      }
      window.__testState['addSourceBufferException'] = true;
    });
  };

  window.__testInitialVideoState = function() {
    var temp_video = createVideo();

    var test_result = isNaN(temp_video.duration);
    test_result &= temp_video.videoWidth === 0;
    test_result &= temp_video.videoHeight === 0;
    test_result &= temp_video.readyState === HTMLMediaElement.HAVE_NOTHING;
    test_result &= temp_video.src === '';
    test_result &= temp_video.currentSrc === '';

    window.__testState['initialVideoState'] = !!test_result;
  };

  window.__testInitialMSState = function() {
    var vm = setupVideoAndMs(
      function() {
        var test_result = true;
        test_result = test_result && isNaN(vm.ms.duration);
        test_result = test_result && vm.ms.readyState === 'open';
        window.__testState['initialMSState'] = test_result;
    });
  };

  function appendTestTemplate(
    test_name, media, test_func, abort, start, length, offset) {
    var vm = setupVideoAndMs(function() {
      var sb = vm.ms.addSourceBuffer(media.format);
      var xhr = new XHRWrapper(
        media.src,
        function(e) {
          var response_data = xhr.getResponseData();

          if (offset != null)
            sb.timestampOffset = offset;

          sb.append(response_data);

          if (abort != null) {
            sb.abort();
            sb.append(response_data);
          }

          var test_result = test_func(sb, media);
          window.__testState[test_name] = test_result;
        },
        function(e) {
          window.__testState[test_name] = false;
        }, start, length);

      xhr.send();
    });
  }

  function appendInnerTest(sb, media) {
    return sb.buffered.length === 1 && sb.buffered.start(0) === 0 &&
      approxEq(sb.buffered.end(0), media.duration);
  }

  window.__testAppend_audio = function() {
    appendTestTemplate('append_audio', audio1MB, appendInnerTest);
  };

  window.__testAppend_video = function() {
    appendTestTemplate('append_video', video1MB, appendInnerTest);
  };

  function appendAbortInnerTest(sb, media) {
    return sb.buffered.length === 1 && sb.buffered.start(0) === 0 &&
      sb.buffered.end(0) > 0;
  }

  window.__testAppendAbort_audio = function() {
    appendTestTemplate(
      'appendAbort_audio', audio1MB, appendAbortInnerTest, true, 0, 200000);
  };

  window.__testAppendAbort_video = function() {
    appendTestTemplate(
      'appendAbort_video', video1MB, appendAbortInnerTest, true, 0, 200000);
  };

  var TIMESTAMP_BUFFERED_OFFSET = 5;
  function appendTimestampOffsetTest(sb, media) {

    return sb.buffered.length === 1 &&
      sb.buffered.start(0) === TIMESTAMP_BUFFERED_OFFSET &&
      approxEq(sb.buffered.end(0), media.duration + TIMESTAMP_BUFFERED_OFFSET);
  }

  window.__testAppendTimestampOffset_audio = function() {
    appendTestTemplate(
      'appendTimestampOffset_audio', audio1MB, appendTimestampOffsetTest,
      null, null, null, TIMESTAMP_BUFFERED_OFFSET);
  };

  window.__testAppendTimestampOffset_video = function() {
    appendTestTemplate(
      'appendTimestampOffset_video', video1MB, appendTimestampOffsetTest,
      null, null, null, TIMESTAMP_BUFFERED_OFFSET);
  };

  window.__testDuration = function() {
    var vm = setupVideoAndMs(
      function() {
        var DURATION_TIME = 10;
        vm.ms.duration = DURATION_TIME;
        window.setTimeout(function() {
            window.__testState['duration'] = vm.ms.duration === DURATION_TIME;
        }, 20);
    });
  };

  function testDurationAfterAppend(test_name, media) {
    var vm = setupVideoAndMs(function() {
      var sb = vm.ms.addSourceBuffer(media.format);

      function onDurationChange() {
        window.__testState[test_name] = approxEq(
          vm.ms.duration, sb.buffered.end(0));
      }

      var xhr = new XHRWrapper(media.src, function() {
          var response_data = xhr.getResponseData();
          sb.append(response_data);
          sb.abort();
          vm.ms.duration = sb.buffered.end(0) / 2;
          vm.video.addEventListener('durationchange', onDurationChange);
          sb.append(response_data);
        });

      xhr.send();
    });
  };

  window.__testDurationAfterAppend_audio = function() {
    testDurationAfterAppend('durationAfterAppend_audio', audio1MB);
  };

  window.__testDurationAfterAppend_video = function() {
    testDurationAfterAppend('durationAfterAppend_video', video1MB);
  };

  window.__testSourceRemove = function() {
    var vm = setupVideoAndMs(
      function() {
        var sbAudio = vm.ms.addSourceBuffer(audio_format);
        var result = vm.ms.sourceBuffers.length === 1;
        vm.ms.removeSourceBuffer(sbAudio);
        result &= vm.ms.sourceBuffers.length === 0;

        sbAudio = vm.ms.addSourceBuffer(audio_format);
        result &= vm.ms.sourceBuffers.length === 1;
        for (var i = 0; i < 10; ++i) {
          var sbVideo = vm.ms.addSourceBuffer(video_format);
          result &= vm.ms.sourceBuffers.length === 2;
          vm.ms.removeSourceBuffer(sbVideo);
          result &= vm.ms.sourceBuffers.length === 1;
        }

        vm.ms.removeSourceBuffer(sbAudio);
        result &= vm.ms.sourceBuffers.length === 0;

        window.__testState['sourceRemove'] = !!result;
    });
  };

  // EME tests.
  window.__testCanPlayWebM = function() {
    var tempVideo = createVideo();
    return tempVideo.canPlayType(
        'video/webm; codecs="vp8,vorbis"') === 'probably' &&
      tempVideo.canPlayType(
        'audio/webm; codecs="vorbis"') === 'probably';
  };

  window.__testCanPlayClearKey = function() {
    var tempVideo = createVideo();
    return tempVideo.canPlayType(
        'video/mp4; codecs="avc1.640028"',
        'webkit-org.w3.clearkey') === 'probably' &&
      tempVideo.canPlayType(
        'audio/mp4; codecs="mp4a.40.2"',
        'webkit-org.w3.clearkey') === 'probably';
  };

  window.__testCanNotPlayPlayReady = function() {
    var tempVideo = createVideo();
    return tempVideo.canPlayType(
        'video/mp4; codecs="avc1.640028"',
        'com.youtube.playready') !== 'probably' &&
      tempVideo.canPlayType(
        'audio/mp4; codecs="mp4a.40.2"',
        'com.youtube.playready') !== 'probably';
  };

  window.__testCanPlayWidevine = function() {
    function createWidevineTest(mediaType) {
      var tempVideo = createVideo();

      return function(codecs, keySystem, criteria) {
        var codecString = mediaType;
        if (codecs != null)
          codecString += '; codecs="' + codecs + '"';

        var testResult = tempVideo.canPlayType(codecString, keySystem);

        if (criteria === null)
          return testResult === 'probably' || testResult === 'maybe';
        else if (typeof(criteria) === 'string')
          return testResult === criteria;
        else if (criteria.length) {
          var checks = false;
          for (var i in criteria)
            checks |= testResult === criteria[i];
          return !!checks;
        }
        return false;
      }
    }

    var audioTest = createWidevineTest('audio/webm');
    var videoTest = createWidevineTest('video/webm');

    var result = true;

    // Supported video formats.
    result &= videoTest(null, 'com.widevine.alpha', 'maybe');
    result &= videoTest(null, 'com.widevine', 'maybe');
    result &= videoTest('vp8', 'com.widevine.alpha', 'probably');
    result &= videoTest('vp8', 'com.widevine', 'probably');
    result &= videoTest('vp8.0', 'com.widevine.alpha', 'probably');
    result &= videoTest('vp8.0', 'com.widevine', 'probably');
    result &= videoTest('vorbis', 'com.widevine.alpha', 'probably');
    result &= videoTest('vorbis', 'com.widevine', 'probably');
    result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine.alpha', 'probably');
    result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine', 'probably');

    // Supported audio formats.
    result &= audioTest(null, 'com.widevine.alpha', 'maybe');
    result &= audioTest(null, 'com.widevine', 'maybe');
    result &= audioTest('vorbis', 'com.widevine.alpha', 'probably');
    result &= audioTest('vorbis', 'com.widevine', 'probably');

    // Unsupported video formats.
    result &= videoTest('codecs="vp8"', 'com.widevine.', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.foo', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.alpha.', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.alpha.foo', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.alph', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.alphb', '');
    result &= videoTest('codecs="vp8"', 'com.widevine.alphaa', '');
    result &= videoTest('codecs="avc1.640028"', 'com.widevine.alpha', '');
    result &= videoTest('codecs="mp4a"', 'com.widevine.alpha', '');

    // Unsported audio formats.
    result &= audioTest('codecs="vp8"', 'com.widevine', '');
    result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', '');
    result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', '');

    return result;
  };
})();