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 this.queueCallback_(() => {
81 this.sourceBuffer_.appendBuffer(bytes);
82 }, done);
83 }
84
85 /**
86 * Indicates what TimeRanges are buffered in the managed SourceBuffer.
87 *
88 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
89 */
90 buffered() {
91 if (!this.sourceBuffer_) {
92 return videojs.createTimeRanges();
93 }
94 return this.sourceBuffer_.buffered;
95 }
96
97 /**
98 * Queue an update to remove a time range from the buffer.
99 *
100 * @param {Number} start where to start the removal
101 * @param {Number} end where to end the removal
102 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
103 */
104 remove(start, end) {
105 if (this.processedAppend_) {
106 this.queueCallback_(() => {
107 this.sourceBuffer_.remove(start, end);
108 }, noop);
109 }
110 }
111
112 /**
113 * Whether the underlying sourceBuffer is updating or not
114 *
115 * @return {Boolean} the updating status of the SourceBuffer
116 */
117 updating() {
118 return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
119 }
120
121 /**
122 * Set/get the timestampoffset on the SourceBuffer
123 *
124 * @return {Number} the timestamp offset
125 */
126 timestampOffset(offset) {
127 if (typeof offset !== 'undefined') {
128 this.queueCallback_(() => {
129 this.sourceBuffer_.timestampOffset = offset;
130 });
131 this.timestampOffset_ = offset;
132 }
133 return this.timestampOffset_;
134 }
135
136 /**
137 * Queue a callback to run
138 */
139 queueCallback_(callback, done) {
140 this.callbacks_.push([callback.bind(this), done]);
141 this.runCallback_();
142 }
143
144 /**
145 * Run a queued callback
146 */
147 runCallback_() {
148 let callbacks;
149
150 if (!this.updating() &&
151 this.callbacks_.length) {
152 callbacks = this.callbacks_.shift();
153 this.pendingCallback_ = callbacks[1];
154 callbacks[0]();
155 }
156 }
157
158 /**
159 * dispose of the source updater and the underlying sourceBuffer
160 */
161 dispose() {
162 this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
163 if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
164 this.sourceBuffer_.abort();
165 }
166 }
167}