• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4"use strict";
5
6(function() {
7  window.__eventReporter = {};
8  window.__testState = {};
9
10  var logger = new Logger();
11  window.__logger = logger;
12
13  var video_format = 'video/mp4; codecs="avc1.640028"';
14  var audio_format = 'audio/mp4; codecs="mp4a.40.2"';
15
16  var audio1MB = createAudioDef(
17      'http://localhost:8000/files/car-audio-1MB-trunc.mp4', 1048576, 65.875);
18  var video1MB = createVideoDef(
19      'http://localhost:8000/files/test-video-1MB.mp4', 1031034, 1.04);
20
21  // Utility functions.
22  function Logger() {
23    var logs = [];
24
25    this.log = function(log_string) {
26      logs.push(log_string);
27    };
28
29    this.toString = function() {
30      var output = '';
31      for (var i in logs)
32        output += logs[i];
33
34      return output;
35    }
36  }
37
38  function createAudioDef(src, size, duration) {
39    return {
40      type: 'audio',
41      format: audio_format,
42      size: size,
43      src: src,
44      duration: duration,
45      bps: Math.floor(size / duration)
46    };
47  }
48
49  function createVideoDef(src, size, duration) {
50    return {
51      type: 'video',
52      format: video_format,
53      size: size,
54      src: src,
55      duration: duration,
56      bps: Math.floor(size / duration)
57    };
58  }
59
60  function createMediaSource() {
61    if (typeof MediaSource !== 'undefined')
62      return new MediaSource();
63    else
64      return new WebKitMediaSource();
65  }
66
67  function createVideo() {
68    return document.createElement('video');
69  }
70
71  function setupVideoAndMs(onSourceopen) {
72    var temp_video = createVideo();
73    var ms = createMediaSource();
74    ms.addEventListener('webkitsourceopen', onSourceopen);
75    temp_video.srcObject = ms;
76    return {
77      'video': temp_video,
78      'ms': ms
79    };
80  }
81
82  function XHRWrapper(file, onLoad, onError, start, length) {
83    self = this;
84
85    self.file = file;
86    self.onLoad = onLoad;
87    self.onError = onError;
88    self.start = start;
89    self.length = length;
90
91    this.getResponseData = function() {
92      var result = new Uint8Array(this.xhr.response);
93      if (start != null) {
94        return result.subarray(start, start + length);
95      }
96      return result;
97    };
98
99    this.abort = function() {
100      this.xhr.abort();
101    };
102
103    this.send = function() {
104      this.xhr.send();
105    };
106
107    this.xhr = new XMLHttpRequest();
108    this.xhr.open('GET', file, true);
109
110    this.xhr.addEventListener('load', function(e) {
111      self.onLoad(e);
112    });
113
114    this.xhr.addEventListener('error', function(e) {
115      logger.log('XHR errored.');
116      self.onError(e);
117    });
118
119    this.xhr.addEventListener('timeout', function(e) {
120      logger.log('XHR timed out.');
121      self.onError(e);
122    });
123
124    this.xhr.responseType = 'arraybuffer';
125    if (length != null) {
126      start = start || 0;
127      this.xhr.setRequestHeader(
128        'Range',
129        'bytes=' + start + '-' + (start + length - 1)
130      );
131    }
132  }
133
134  function approxEq(a, b) {
135    return Math.abs(a - b) < 0.5;
136  };
137
138  // MSE tests.
139  window.__testAttach = function() {
140    var ms = createMediaSource();
141    ms.addEventListener('webkitsourceopen', function() {
142      window.__eventReporter['sourceopen'] = true;
143    });
144
145    var video = document.getElementById('main_player');
146    video.srcObject = ms;
147    video.load();
148  };
149
150  window.__testAddSourceBuffer = function() {
151    var vm = setupVideoAndMs(function() {
152      try {
153        var return_value = true;
154        return_value &= vm.ms.sourceBuffers.length === 0;
155        vm.ms.addSourceBuffer(audio_format);
156        return_value &= vm.ms.sourceBuffers.length === 1;
157        vm.ms.addSourceBuffer(video_format);
158        return_value &= vm.ms.sourceBuffers.length === 2;
159
160        window.__testState['addSourceBuffer'] = !!return_value;
161      }
162      catch (e) {
163        window.__testState['addSourceBuffer'] = false;
164      }
165    });
166  };
167
168  window.__testAddSupportedFormats = function() {
169    var formats = [
170      audio_format,
171      video_format,
172    ];
173
174    var vm = setupVideoAndMs(function() {
175      for (var i = 0; i < formats.length; ++i) {
176        try {
177          vm.ms.addSourceBuffer(formats[i]);
178        } catch (e) {
179          window.__testState['addSupportedFormats'] = false;
180          return;
181        }
182      }
183      window.__testState['addSupportedFormats'] = true;
184    });
185  };
186
187  window.__testAddSourceBufferException = function() {
188    var vm = setupVideoAndMs(function() {
189      try {
190        vm.ms.addSourceBuffer('^^^');
191        window.__testState['addSourceBufferException'] = false;
192        return;
193      }
194      catch (e) {
195        if (e.code !== DOMException.NOT_SUPPORTED_ERR) {
196          window.__testState['addSourceBufferException'] = false;
197          return;
198        }
199      }
200
201      try {
202        var temp_media_source = new WebKitMediaSource();
203        temp_media_source.addSourceBuffer(audio_format);
204        window.__testState['addSourceBufferException'] = false;
205        return;
206      }
207      catch (e) {
208        if (e.code !== DOMException.INVALID_STATE_ERR) {
209          window.__testState['addSourceBufferException'] = false;
210          return;
211        }
212      }
213      window.__testState['addSourceBufferException'] = true;
214    });
215  };
216
217  window.__testInitialVideoState = function() {
218    var temp_video = createVideo();
219
220    var test_result = isNaN(temp_video.duration);
221    test_result &= temp_video.videoWidth === 0;
222    test_result &= temp_video.videoHeight === 0;
223    test_result &= temp_video.readyState === HTMLMediaElement.HAVE_NOTHING;
224    test_result &= temp_video.src === '';
225    test_result &= temp_video.currentSrc === '';
226
227    window.__testState['initialVideoState'] = !!test_result;
228  };
229
230  window.__testInitialMSState = function() {
231    var vm = setupVideoAndMs(
232      function() {
233        var test_result = true;
234        test_result = test_result && isNaN(vm.ms.duration);
235        test_result = test_result && vm.ms.readyState === 'open';
236        window.__testState['initialMSState'] = test_result;
237    });
238  };
239
240  function appendTestTemplate(
241    test_name, media, test_func, abort, start, length, offset) {
242    var vm = setupVideoAndMs(function() {
243      var sb = vm.ms.addSourceBuffer(media.format);
244      var xhr = new XHRWrapper(
245        media.src,
246        function(e) {
247          var response_data = xhr.getResponseData();
248
249          if (offset != null)
250            sb.timestampOffset = offset;
251
252          sb.append(response_data);
253
254          if (abort != null) {
255            sb.abort();
256            sb.append(response_data);
257          }
258
259          var test_result = test_func(sb, media);
260          window.__testState[test_name] = test_result;
261        },
262        function(e) {
263          window.__testState[test_name] = false;
264        }, start, length);
265
266      xhr.send();
267    });
268  }
269
270  function appendInnerTest(sb, media) {
271    return sb.buffered.length === 1 && sb.buffered.start(0) === 0 &&
272      approxEq(sb.buffered.end(0), media.duration);
273  }
274
275  window.__testAppend_audio = function() {
276    appendTestTemplate('append_audio', audio1MB, appendInnerTest);
277  };
278
279  window.__testAppend_video = function() {
280    appendTestTemplate('append_video', video1MB, appendInnerTest);
281  };
282
283  function appendAbortInnerTest(sb, media) {
284    return sb.buffered.length === 1 && sb.buffered.start(0) === 0 &&
285      sb.buffered.end(0) > 0;
286  }
287
288  window.__testAppendAbort_audio = function() {
289    appendTestTemplate(
290      'appendAbort_audio', audio1MB, appendAbortInnerTest, true, 0, 200000);
291  };
292
293  window.__testAppendAbort_video = function() {
294    appendTestTemplate(
295      'appendAbort_video', video1MB, appendAbortInnerTest, true, 0, 200000);
296  };
297
298  var TIMESTAMP_BUFFERED_OFFSET = 5;
299  function appendTimestampOffsetTest(sb, media) {
300
301    return sb.buffered.length === 1 &&
302      sb.buffered.start(0) === TIMESTAMP_BUFFERED_OFFSET &&
303      approxEq(sb.buffered.end(0), media.duration + TIMESTAMP_BUFFERED_OFFSET);
304  }
305
306  window.__testAppendTimestampOffset_audio = function() {
307    appendTestTemplate(
308      'appendTimestampOffset_audio', audio1MB, appendTimestampOffsetTest,
309      null, null, null, TIMESTAMP_BUFFERED_OFFSET);
310  };
311
312  window.__testAppendTimestampOffset_video = function() {
313    appendTestTemplate(
314      'appendTimestampOffset_video', video1MB, appendTimestampOffsetTest,
315      null, null, null, TIMESTAMP_BUFFERED_OFFSET);
316  };
317
318  window.__testDuration = function() {
319    var vm = setupVideoAndMs(
320      function() {
321        var DURATION_TIME = 10;
322        vm.ms.duration = DURATION_TIME;
323        window.setTimeout(function() {
324            window.__testState['duration'] = vm.ms.duration === DURATION_TIME;
325        }, 20);
326    });
327  };
328
329  function testDurationAfterAppend(test_name, media) {
330    var vm = setupVideoAndMs(function() {
331      var sb = vm.ms.addSourceBuffer(media.format);
332
333      function onDurationChange() {
334        window.__testState[test_name] = approxEq(
335          vm.ms.duration, sb.buffered.end(0));
336      }
337
338      var xhr = new XHRWrapper(media.src, function() {
339          var response_data = xhr.getResponseData();
340          sb.append(response_data);
341          sb.abort();
342          vm.ms.duration = sb.buffered.end(0) / 2;
343          vm.video.addEventListener('durationchange', onDurationChange);
344          sb.append(response_data);
345        });
346
347      xhr.send();
348    });
349  };
350
351  window.__testDurationAfterAppend_audio = function() {
352    testDurationAfterAppend('durationAfterAppend_audio', audio1MB);
353  };
354
355  window.__testDurationAfterAppend_video = function() {
356    testDurationAfterAppend('durationAfterAppend_video', video1MB);
357  };
358
359  window.__testSourceRemove = function() {
360    var vm = setupVideoAndMs(
361      function() {
362        var sbAudio = vm.ms.addSourceBuffer(audio_format);
363        var result = vm.ms.sourceBuffers.length === 1;
364        vm.ms.removeSourceBuffer(sbAudio);
365        result &= vm.ms.sourceBuffers.length === 0;
366
367        sbAudio = vm.ms.addSourceBuffer(audio_format);
368        result &= vm.ms.sourceBuffers.length === 1;
369        for (var i = 0; i < 10; ++i) {
370          var sbVideo = vm.ms.addSourceBuffer(video_format);
371          result &= vm.ms.sourceBuffers.length === 2;
372          vm.ms.removeSourceBuffer(sbVideo);
373          result &= vm.ms.sourceBuffers.length === 1;
374        }
375
376        vm.ms.removeSourceBuffer(sbAudio);
377        result &= vm.ms.sourceBuffers.length === 0;
378
379        window.__testState['sourceRemove'] = !!result;
380    });
381  };
382
383  // EME tests.
384  window.__testCanPlayWebM = function() {
385    var tempVideo = createVideo();
386    return tempVideo.canPlayType(
387        'video/webm; codecs="vp8,vorbis"') === 'probably' &&
388      tempVideo.canPlayType(
389        'audio/webm; codecs="vorbis"') === 'probably';
390  };
391
392  window.__testCanPlayClearKey = function() {
393    var tempVideo = createVideo();
394    return tempVideo.canPlayType(
395        'video/mp4; codecs="avc1.640028"',
396        'webkit-org.w3.clearkey') === 'probably' &&
397      tempVideo.canPlayType(
398        'audio/mp4; codecs="mp4a.40.2"',
399        'webkit-org.w3.clearkey') === 'probably';
400  };
401
402  window.__testCanNotPlayPlayReady = function() {
403    var tempVideo = createVideo();
404    return tempVideo.canPlayType(
405        'video/mp4; codecs="avc1.640028"',
406        'com.youtube.playready') !== 'probably' &&
407      tempVideo.canPlayType(
408        'audio/mp4; codecs="mp4a.40.2"',
409        'com.youtube.playready') !== 'probably';
410  };
411
412  window.__testCanPlayWidevine = function() {
413    function createWidevineTest(mediaType) {
414      var tempVideo = createVideo();
415
416      return function(codecs, keySystem, criteria) {
417        var codecString = mediaType;
418        if (codecs != null)
419          codecString += '; codecs="' + codecs + '"';
420
421        var testResult = tempVideo.canPlayType(codecString, keySystem);
422
423        if (criteria === null)
424          return testResult === 'probably' || testResult === 'maybe';
425        else if (typeof(criteria) === 'string')
426          return testResult === criteria;
427        else if (criteria.length) {
428          var checks = false;
429          for (var i in criteria)
430            checks |= testResult === criteria[i];
431          return !!checks;
432        }
433        return false;
434      }
435    }
436
437    var audioTest = createWidevineTest('audio/webm');
438    var videoTest = createWidevineTest('video/webm');
439
440    var result = true;
441
442    // Supported video formats.
443    result &= videoTest(null, 'com.widevine.alpha', 'maybe');
444    result &= videoTest(null, 'com.widevine', 'maybe');
445    result &= videoTest('vp8', 'com.widevine.alpha', 'probably');
446    result &= videoTest('vp8', 'com.widevine', 'probably');
447    result &= videoTest('vp8.0', 'com.widevine.alpha', 'probably');
448    result &= videoTest('vp8.0', 'com.widevine', 'probably');
449    result &= videoTest('vorbis', 'com.widevine.alpha', 'probably');
450    result &= videoTest('vorbis', 'com.widevine', 'probably');
451    result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine.alpha', 'probably');
452    result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine', 'probably');
453
454    // Supported audio formats.
455    result &= audioTest(null, 'com.widevine.alpha', 'maybe');
456    result &= audioTest(null, 'com.widevine', 'maybe');
457    result &= audioTest('vorbis', 'com.widevine.alpha', 'probably');
458    result &= audioTest('vorbis', 'com.widevine', 'probably');
459
460    // Unsupported video formats.
461    result &= videoTest('codecs="vp8"', 'com.widevine.', '');
462    result &= videoTest('codecs="vp8"', 'com.widevine.foo', '');
463    result &= videoTest('codecs="vp8"', 'com.widevine.alpha.', '');
464    result &= videoTest('codecs="vp8"', 'com.widevine.alpha.foo', '');
465    result &= videoTest('codecs="vp8"', 'com.widevine.alph', '');
466    result &= videoTest('codecs="vp8"', 'com.widevine.alphb', '');
467    result &= videoTest('codecs="vp8"', 'com.widevine.alphaa', '');
468    result &= videoTest('codecs="avc1.640028"', 'com.widevine.alpha', '');
469    result &= videoTest('codecs="mp4a"', 'com.widevine.alpha', '');
470
471    // Unsported audio formats.
472    result &= audioTest('codecs="vp8"', 'com.widevine', '');
473    result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', '');
474    result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', '');
475
476    return result;
477  };
478})();
479