Land Recent QUIC Changes.
[chromium-blink-merge.git] / remoting / webapp / media_source_renderer.js
blobb9cc24c611ebcc4daf311ec46eae1851afa867db
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.
5 'use strict';
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
10 /**
11  * @param {HTMLMediaElement} videoTag <video> tag to render to.
12  * @constructor
13  */
14 remoting.MediaSourceRenderer = function(videoTag) {
15   /** @type {HTMLMediaElement} */
16   this.video_ = videoTag;
18   /** @type {MediaSource} */
19   this.mediaSource_ = null;
21   /** @type {SourceBuffer} */
22   this.sourceBuffer_ = null;
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_ = [];
29   this.lastKeyFramePos_ = 0;
32 /**
33  * @param {string} format Format of the stream.
34  */
35 remoting.MediaSourceRenderer.prototype.reset = function(format) {
36   // Reset the queue.
37   this.buffers_ = [];
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   });
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();
58 /**
59  * @param {string} format
60  * @private
61  */
62 remoting.MediaSourceRenderer.prototype.onSourceOpen_ = function(format) {
63   this.sourceBuffer_ =
64       this.mediaSource_.addSourceBuffer(format);
66   this.sourceBuffer_.addEventListener(
67       'updateend', this.processPendingData_.bind(this));
68   this.processPendingData_();
71 /**
72  * @private
73  */
74 remoting.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           }
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   }
104  * @param {ArrayBuffer} data
105  * @param {boolean} keyframe
106  */
107 remoting.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_();