• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 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
5'use strict';
6
7/** @suppress {duplicate} */
8var remoting = remoting || {};
9
10/**
11 * @param {HTMLMediaElement} videoTag <video> tag to render to.
12 * @constructor
13 */
14remoting.MediaSourceRenderer = function(videoTag) {
15  /** @type {HTMLMediaElement} */
16  this.video_ = videoTag;
17
18  /** @type {MediaSource} */
19  this.mediaSource_ = null;
20
21  /** @type {SourceBuffer} */
22  this.sourceBuffer_ = null;
23
24  /** @type {!Array.<ArrayBuffer>} Queue of pending buffers that haven't been
25   * processed. A null element indicates that the SourceBuffer can be reset
26   * because the following buffer contains a keyframe. */
27  this.buffers_ = [];
28
29  this.lastKeyFramePos_ = 0;
30}
31
32/**
33 * @param {string} format Format of the stream.
34 */
35remoting.MediaSourceRenderer.prototype.reset = function(format) {
36  // Reset the queue.
37  this.buffers_ = [];
38
39  // Create a new MediaSource instance.
40  this.sourceBuffer_ = null;
41  this.mediaSource_ = new MediaSource();
42  this.mediaSource_.addEventListener('sourceopen',
43                                     this.onSourceOpen_.bind(this, format));
44  this.mediaSource_.addEventListener('sourceclose', function(e) {
45    console.error("MediaSource closed unexpectedly.");
46  });
47  this.mediaSource_.addEventListener('sourceended', function(e) {
48    console.error("MediaSource ended unexpectedly.");
49  });
50
51  // Start playback from new MediaSource.
52  this.video_.src =
53      /** @type {string} */(
54          window.URL.createObjectURL(/** @type {!Blob} */(this.mediaSource_)));
55  this.video_.play();
56}
57
58/**
59 * @param {string} format
60 * @private
61 */
62remoting.MediaSourceRenderer.prototype.onSourceOpen_ = function(format) {
63  this.sourceBuffer_ =
64      this.mediaSource_.addSourceBuffer(format);
65
66  this.sourceBuffer_.addEventListener(
67      'updateend', this.processPendingData_.bind(this));
68  this.processPendingData_();
69}
70
71/**
72 * @private
73 */
74remoting.MediaSourceRenderer.prototype.processPendingData_ = function() {
75  if (this.sourceBuffer_) {
76    while (this.buffers_.length > 0 && !this.sourceBuffer_.updating) {
77      var buffer = /** @type {ArrayBuffer} */ this.buffers_.shift();
78      if (buffer == null) {
79        // Remove data from the SourceBuffer from the beginning to the previous
80        // key frame. By default Chrome buffers up to 150MB of data. We never
81        // need to seek the stream, so it doesn't make sense to keep any of that
82        // data.
83        if (this.sourceBuffer_.buffered.length > 0) {
84          // TODO(sergeyu): Check currentTime to make sure that the current
85          // playback position is not being removed. crbug.com/398290 .
86          if (this.lastKeyFramePos_ > this.sourceBuffer_.buffered.start(0)) {
87            this.sourceBuffer_.remove(this.sourceBuffer_.buffered.start(0),
88                                      this.lastKeyFramePos_);
89          }
90
91          this.lastKeyFramePos_ = this.sourceBuffer_.buffered.end(
92              this.sourceBuffer_.buffered.length - 1);
93        }
94      } else {
95        // TODO(sergeyu): Figure out the way to determine when a frame is
96        // rendered and use it to report performance statistics.
97        this.sourceBuffer_.appendBuffer(buffer);
98      }
99    }
100  }
101}
102
103/**
104 * @param {ArrayBuffer} data
105 * @param {boolean} keyframe
106 */
107remoting.MediaSourceRenderer.prototype.onIncomingData =
108    function(data, keyframe) {
109  if (keyframe) {
110    // Queue SourceBuffer reset request.
111    this.buffers_.push(null);
112  }
113  this.buffers_.push(data);
114  this.processPendingData_();
115}
116
117