UNPKG

4.38 kBJavaScriptView Raw
1/**
2 * @file source-updater.js
3 */
4import videojs from 'video.js';
5
6const noop = function() {};
7
8/**
9 * A queue of callbacks to be serialized and applied when a
10 * MediaSource and its associated SourceBuffers are not in the
11 * updating state. It is used by the segment loader to update the
12 * underlying SourceBuffers when new data is loaded, for instance.
13 *
14 * @class SourceUpdater
15 * @param {MediaSource} mediaSource the MediaSource to create the
16 * SourceBuffer from
17 * @param {String} mimeType the desired MIME type of the underlying
18 * SourceBuffer
19 */
20export default class SourceUpdater {
21 constructor(mediaSource, mimeType) {
22 let createSourceBuffer = () => {
23 this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType);
24
25 // run completion handlers and process callbacks as updateend
26 // events fire
27 this.onUpdateendCallback_ = () => {
28 let pendingCallback = this.pendingCallback_;
29
30 this.pendingCallback_ = null;
31
32 if (pendingCallback) {
33 pendingCallback();
34 }
35
36 this.runCallback_();
37 };
38
39 this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
40
41 this.runCallback_();
42 };
43
44 this.callbacks_ = [];
45 this.pendingCallback_ = null;
46 this.timestampOffset_ = 0;
47 this.mediaSource = mediaSource;
48 this.processedAppend_ = false;
49
50 if (mediaSource.readyState === 'closed') {
51 mediaSource.addEventListener('sourceopen', createSourceBuffer);
52 } else {
53 createSourceBuffer();
54 }
55 }
56
57 /**
58 * Aborts the current segment and resets the segment parser.
59 *
60 * @param {Function} done function to call when done
61 * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
62 */
63 abort(done) {
64 if (this.processedAppend_) {
65 this.queueCallback_(() => {
66 this.sourceBuffer_.abort();
67 }, done);
68 }
69 }
70
71 /**
72 * Queue an update to append an ArrayBuffer.
73 *
74 * @param {ArrayBuffer} bytes
75 * @param {Function} done the function to call when done
76 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
77 */
78 appendBuffer(bytes, done) {
79 this.processedAppend_ = true;
80
81 this.queueCallback_(() => {
82 this.sourceBuffer_.appendBuffer(bytes);
83 }, done);
84 }
85
86 /**
87 * Indicates what TimeRanges are buffered in the managed SourceBuffer.
88 *
89 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
90 */
91 buffered() {
92 if (!this.sourceBuffer_) {
93 return videojs.createTimeRanges();
94 }
95 return this.sourceBuffer_.buffered;
96 }
97
98 /**
99 * Queue an update to remove a time range from the buffer.
100 *
101 * @param {Number} start where to start the removal
102 * @param {Number} end where to end the removal
103 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
104 */
105 remove(start, end) {
106 if (this.processedAppend_) {
107 this.queueCallback_(() => {
108 this.sourceBuffer_.remove(start, end);
109 }, noop);
110 }
111 }
112
113 /**
114 * Whether the underlying sourceBuffer is updating or not
115 *
116 * @return {Boolean} the updating status of the SourceBuffer
117 */
118 updating() {
119 return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
120 }
121
122 /**
123 * Set/get the timestampoffset on the SourceBuffer
124 *
125 * @return {Number} the timestamp offset
126 */
127 timestampOffset(offset) {
128 if (typeof offset !== 'undefined') {
129 this.queueCallback_(() => {
130 this.sourceBuffer_.timestampOffset = offset;
131 });
132 this.timestampOffset_ = offset;
133 }
134 return this.timestampOffset_;
135 }
136
137 /**
138 * Queue a callback to run
139 */
140 queueCallback_(callback, done) {
141 this.callbacks_.push([callback.bind(this), done]);
142 this.runCallback_();
143 }
144
145 /**
146 * Run a queued callback
147 */
148 runCallback_() {
149 let callbacks;
150
151 if (!this.updating() &&
152 this.callbacks_.length) {
153 callbacks = this.callbacks_.shift();
154 this.pendingCallback_ = callbacks[1];
155 callbacks[0]();
156 }
157 }
158
159 /**
160 * dispose of the source updater and the underlying sourceBuffer
161 */
162 dispose() {
163 this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
164 if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
165 this.sourceBuffer_.abort();
166 }
167 }
168}