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