f5990fce69e984443b86924b1e488fc685af6876
[platform/framework/web/crosswalk.git] / src / remoting / webapp / media_source_renderer.js
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} */
8 var remoting = remoting || {};
9
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;
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
30 /**
31  * @param {string} format Format of the stream.
32  */
33 remoting.MediaSourceRenderer.prototype.reset = function(format) {
34   // Reset the queue.
35   this.buffers_ = [];
36
37   // Create a new MediaSource instance.
38   this.sourceBuffer_ = null;
39   this.mediaSource_ = new MediaSource();
40   this.mediaSource_.addEventListener('sourceopen',
41                                      this.onSourceOpen_.bind(this, format));
42   this.mediaSource_.addEventListener('sourceclose', function(e) {
43     console.error("MediaSource closed unexpectedly.");
44   });
45   this.mediaSource_.addEventListener('sourceended', function(e) {
46     console.error("MediaSource ended unexpectedly.");
47   });
48
49   // Start playback from new MediaSource.
50   this.video_.src =
51       /** @type {string} */(
52           window.URL.createObjectURL(/** @type {!Blob} */(this.mediaSource_)));
53   this.video_.play();
54 }
55
56 /**
57  * @param {string} format
58  * @private
59  */
60 remoting.MediaSourceRenderer.prototype.onSourceOpen_ = function(format) {
61   this.sourceBuffer_ =
62       this.mediaSource_.addSourceBuffer(format);
63
64   this.sourceBuffer_.addEventListener(
65       'updateend', this.processPendingData_.bind(this));
66   this.processPendingData_();
67 }
68
69 /**
70  * @private
71  */
72 remoting.MediaSourceRenderer.prototype.processPendingData_ = function() {
73   if (this.sourceBuffer_) {
74     while (this.buffers_.length > 0 && !this.sourceBuffer_.updating) {
75       var buffer = /** @type {ArrayBuffer} */ this.buffers_.shift();
76       if (buffer == null) {
77         // Remove all data from the SourceBuffer. By default Chrome buffers up
78         // 150MB of data in SourceBuffer. We never need to seek the stream, so
79         // it doesn't make sense to keep any of that data.
80         if (this.sourceBuffer_.buffered.length > 0) {
81           this.sourceBuffer_.remove(
82               this.sourceBuffer_.buffered.start(0),
83               this.sourceBuffer_.buffered.end(
84                   this.sourceBuffer_.buffered.length - 1));
85         }
86       } else {
87         // TODO(sergeyu): Figure out the way to determine when a frame is
88         // rendered and use it to report performance statistics.
89         this.sourceBuffer_.appendBuffer(buffer);
90       }
91     }
92   }
93 }
94
95 /**
96  * @param {ArrayBuffer} data
97  * @param {boolean} keyframe
98  */
99 remoting.MediaSourceRenderer.prototype.onIncomingData =
100     function(data, keyframe) {
101   if (keyframe) {
102     // Queue SourceBuffer reset request.
103     this.buffers_.push(null);
104   }
105   this.buffers_.push(data);
106   this.processPendingData_();
107 }
108