@alessmicrosystems/mpegts.js 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +158 -0
- package/README_ja.md +153 -0
- package/README_zh.md +157 -0
- package/d.ts/mpegts.d.ts +524 -0
- package/d.ts/src/core/mse-events.d.ts +9 -0
- package/d.ts/src/core/transmuxing-events.d.ts +24 -0
- package/d.ts/src/demux/aac.d.ts +44 -0
- package/d.ts/src/demux/ac3.d.ts +70 -0
- package/d.ts/src/demux/av1-parser.d.ts +77 -0
- package/d.ts/src/demux/av1.d.ts +11 -0
- package/d.ts/src/demux/base-demuxer.d.ts +55 -0
- package/d.ts/src/demux/h264.d.ts +40 -0
- package/d.ts/src/demux/h265.d.ts +65 -0
- package/d.ts/src/demux/klv.d.ts +17 -0
- package/d.ts/src/demux/mp3.d.ts +6 -0
- package/d.ts/src/demux/mpeg4-audio.d.ts +28 -0
- package/d.ts/src/demux/pat-pmt-pes.d.ts +106 -0
- package/d.ts/src/demux/patpmt.d.ts +40 -0
- package/d.ts/src/demux/pes-private-data.d.ts +14 -0
- package/d.ts/src/demux/pgs-data.d.ts +9 -0
- package/d.ts/src/demux/scte35.d.ts +250 -0
- package/d.ts/src/demux/sei.d.ts +8 -0
- package/d.ts/src/demux/smpte2038.d.ts +22 -0
- package/d.ts/src/demux/ts-demuxer.d.ts +124 -0
- package/d.ts/src/player/live-latency-chaser.d.ts +10 -0
- package/d.ts/src/player/live-latency-synchronizer.d.ts +10 -0
- package/d.ts/src/player/loading-controller.d.ts +19 -0
- package/d.ts/src/player/mse-player.d.ts +30 -0
- package/d.ts/src/player/player-engine-dedicated-thread-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine-dedicated-thread.d.ts +48 -0
- package/d.ts/src/player/player-engine-main-thread.d.ts +50 -0
- package/d.ts/src/player/player-engine-worker-cmd-def.d.ts +25 -0
- package/d.ts/src/player/player-engine-worker-msg-def.d.ts +54 -0
- package/d.ts/src/player/player-engine-worker.d.ts +2 -0
- package/d.ts/src/player/player-engine.d.ts +16 -0
- package/d.ts/src/player/player-events.d.ts +21 -0
- package/d.ts/src/player/seeking-handler.d.ts +22 -0
- package/d.ts/src/player/startup-stall-jumper.d.ts +14 -0
- package/d.ts/src/utils/typedarray-equality.d.ts +2 -0
- package/dist/mpegts.js +3 -0
- package/dist/mpegts.js.LICENSE.txt +7 -0
- package/dist/mpegts.js.map +1 -0
- package/package.json +53 -0
- package/src/config.js +67 -0
- package/src/core/features.js +88 -0
- package/src/core/media-info.js +127 -0
- package/src/core/media-segment-info.js +230 -0
- package/src/core/mse-controller.js +599 -0
- package/src/core/mse-events.ts +28 -0
- package/src/core/transmuxer.js +346 -0
- package/src/core/transmuxing-controller.js +628 -0
- package/src/core/transmuxing-events.ts +43 -0
- package/src/core/transmuxing-worker.js +286 -0
- package/src/demux/aac.ts +397 -0
- package/src/demux/ac3.ts +335 -0
- package/src/demux/amf-parser.js +243 -0
- package/src/demux/av1-parser.ts +629 -0
- package/src/demux/av1.ts +103 -0
- package/src/demux/base-demuxer.ts +69 -0
- package/src/demux/demux-errors.js +26 -0
- package/src/demux/exp-golomb.js +116 -0
- package/src/demux/flv-demuxer.js +1854 -0
- package/src/demux/h264.ts +187 -0
- package/src/demux/h265-parser.js +501 -0
- package/src/demux/h265.ts +214 -0
- package/src/demux/klv.ts +40 -0
- package/src/demux/mp3.ts +7 -0
- package/src/demux/mpeg4-audio.ts +45 -0
- package/src/demux/pat-pmt-pes.ts +132 -0
- package/src/demux/pes-private-data.ts +16 -0
- package/src/demux/pgs-data.ts +11 -0
- package/src/demux/scte35.ts +723 -0
- package/src/demux/sei.ts +99 -0
- package/src/demux/smpte2038.ts +89 -0
- package/src/demux/sps-parser.js +298 -0
- package/src/demux/ts-demuxer.ts +2405 -0
- package/src/index.js +4 -0
- package/src/io/fetch-stream-loader.js +266 -0
- package/src/io/io-controller.js +647 -0
- package/src/io/loader.js +134 -0
- package/src/io/param-seek-handler.js +85 -0
- package/src/io/range-seek-handler.js +52 -0
- package/src/io/speed-sampler.js +93 -0
- package/src/io/websocket-loader.js +151 -0
- package/src/io/xhr-moz-chunked-loader.js +211 -0
- package/src/io/xhr-msstream-loader.js +307 -0
- package/src/io/xhr-range-loader.js +366 -0
- package/src/mpegts.js +95 -0
- package/src/player/live-latency-chaser.ts +66 -0
- package/src/player/live-latency-synchronizer.ts +79 -0
- package/src/player/loading-controller.ts +142 -0
- package/src/player/mse-player.ts +150 -0
- package/src/player/native-player.js +262 -0
- package/src/player/player-engine-dedicated-thread.ts +479 -0
- package/src/player/player-engine-main-thread.ts +463 -0
- package/src/player/player-engine-worker-cmd-def.ts +62 -0
- package/src/player/player-engine-worker-msg-def.ts +102 -0
- package/src/player/player-engine-worker.ts +370 -0
- package/src/player/player-engine.ts +35 -0
- package/src/player/player-errors.js +39 -0
- package/src/player/player-events.ts +40 -0
- package/src/player/seeking-handler.ts +205 -0
- package/src/player/startup-stall-jumper.ts +86 -0
- package/src/remux/aac-silent.js +56 -0
- package/src/remux/mp4-generator.js +866 -0
- package/src/remux/mp4-remuxer.js +778 -0
- package/src/utils/browser.js +128 -0
- package/src/utils/exception.js +73 -0
- package/src/utils/logger.js +140 -0
- package/src/utils/logging-control.js +165 -0
- package/src/utils/polyfill.js +68 -0
- package/src/utils/typedarray-equality.ts +69 -0
- package/src/utils/utf8-conv.js +84 -0
- package/src/utils/webworkify-webpack.js +202 -0
- package/tsconfig.json +16 -0
- package/tslint.json +1 -0
- package/types/index.d.ts +3 -0
- package/types/test-flv.ts +8 -0
- package/types/tsconfig.json +24 -0
- package/webpack.config.js +55 -0
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* @author zheng qian <xqq@xqq.im>
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import EventEmitter from 'events';
|
|
20
|
+
import Log from '../utils/logger.js';
|
|
21
|
+
import Browser from '../utils/browser.js';
|
|
22
|
+
import MediaInfo from './media-info.js';
|
|
23
|
+
import FLVDemuxer from '../demux/flv-demuxer.js';
|
|
24
|
+
import TSDemuxer from '../demux/ts-demuxer';
|
|
25
|
+
import MP4Remuxer from '../remux/mp4-remuxer.js';
|
|
26
|
+
import DemuxErrors from '../demux/demux-errors.js';
|
|
27
|
+
import IOController from '../io/io-controller.js';
|
|
28
|
+
import TransmuxingEvents from './transmuxing-events';
|
|
29
|
+
import {LoaderStatus, LoaderErrors} from '../io/loader.js';
|
|
30
|
+
|
|
31
|
+
// Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support
|
|
32
|
+
class TransmuxingController {
|
|
33
|
+
|
|
34
|
+
constructor(mediaDataSource, config) {
|
|
35
|
+
this.TAG = 'TransmuxingController';
|
|
36
|
+
this._emitter = new EventEmitter();
|
|
37
|
+
|
|
38
|
+
this._config = config;
|
|
39
|
+
|
|
40
|
+
// treat single part media as multipart media, which has only one segment
|
|
41
|
+
if (!mediaDataSource.segments) {
|
|
42
|
+
mediaDataSource.segments = [{
|
|
43
|
+
duration: mediaDataSource.duration,
|
|
44
|
+
filesize: mediaDataSource.filesize,
|
|
45
|
+
url: mediaDataSource.url
|
|
46
|
+
}];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// fill in default IO params if not exists
|
|
50
|
+
if (typeof mediaDataSource.cors !== 'boolean') {
|
|
51
|
+
mediaDataSource.cors = true;
|
|
52
|
+
}
|
|
53
|
+
if (typeof mediaDataSource.withCredentials !== 'boolean') {
|
|
54
|
+
mediaDataSource.withCredentials = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this._mediaDataSource = mediaDataSource;
|
|
58
|
+
this._currentSegmentIndex = 0;
|
|
59
|
+
let totalDuration = 0;
|
|
60
|
+
|
|
61
|
+
this._mediaDataSource.segments.forEach((segment) => {
|
|
62
|
+
// timestampBase for each segment, and calculate total duration
|
|
63
|
+
segment.timestampBase = totalDuration;
|
|
64
|
+
totalDuration += segment.duration;
|
|
65
|
+
// params needed by IOController
|
|
66
|
+
segment.cors = mediaDataSource.cors;
|
|
67
|
+
segment.withCredentials = mediaDataSource.withCredentials;
|
|
68
|
+
// referrer policy control, if exist
|
|
69
|
+
if (config.referrerPolicy) {
|
|
70
|
+
segment.referrerPolicy = config.referrerPolicy;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) {
|
|
75
|
+
this._mediaDataSource.duration = totalDuration;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._mediaInfo = null;
|
|
79
|
+
this._demuxer = null;
|
|
80
|
+
this._remuxer = null;
|
|
81
|
+
this._ioctl = null;
|
|
82
|
+
|
|
83
|
+
this._pendingSeekTime = null;
|
|
84
|
+
this._pendingResolveSeekPoint = null;
|
|
85
|
+
|
|
86
|
+
this._statisticsReporter = null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
destroy() {
|
|
90
|
+
this._mediaInfo = null;
|
|
91
|
+
this._mediaDataSource = null;
|
|
92
|
+
|
|
93
|
+
if (this._statisticsReporter) {
|
|
94
|
+
this._disableStatisticsReporter();
|
|
95
|
+
}
|
|
96
|
+
if (this._ioctl) {
|
|
97
|
+
this._ioctl.destroy();
|
|
98
|
+
this._ioctl = null;
|
|
99
|
+
}
|
|
100
|
+
if (this._demuxer) {
|
|
101
|
+
this._demuxer.destroy();
|
|
102
|
+
this._demuxer = null;
|
|
103
|
+
}
|
|
104
|
+
if (this._remuxer) {
|
|
105
|
+
this._remuxer.destroy();
|
|
106
|
+
this._remuxer = null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this._emitter.removeAllListeners();
|
|
110
|
+
this._emitter = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
on(event, listener) {
|
|
114
|
+
this._emitter.addListener(event, listener);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
off(event, listener) {
|
|
118
|
+
this._emitter.removeListener(event, listener);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
start() {
|
|
122
|
+
this._loadSegment(0);
|
|
123
|
+
this._enableStatisticsReporter();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_loadSegment(segmentIndex, optionalFrom) {
|
|
127
|
+
this._currentSegmentIndex = segmentIndex;
|
|
128
|
+
let dataSource = this._mediaDataSource.segments[segmentIndex];
|
|
129
|
+
|
|
130
|
+
let ioctl = this._ioctl = new IOController(dataSource, this._config, segmentIndex);
|
|
131
|
+
ioctl.onError = this._onIOException.bind(this);
|
|
132
|
+
ioctl.onSeeked = this._onIOSeeked.bind(this);
|
|
133
|
+
ioctl.onComplete = this._onIOComplete.bind(this);
|
|
134
|
+
ioctl.onRedirect = this._onIORedirect.bind(this);
|
|
135
|
+
ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this);
|
|
136
|
+
|
|
137
|
+
if (optionalFrom) {
|
|
138
|
+
this._demuxer.bindDataSource(this._ioctl);
|
|
139
|
+
} else {
|
|
140
|
+
ioctl.onDataArrival = this._onInitChunkArrival.bind(this);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
ioctl.open(optionalFrom);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
stop() {
|
|
147
|
+
this._internalAbort();
|
|
148
|
+
this._disableStatisticsReporter();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
_internalAbort() {
|
|
152
|
+
if (this._ioctl) {
|
|
153
|
+
this._ioctl.destroy();
|
|
154
|
+
this._ioctl = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
pause() { // take a rest
|
|
159
|
+
if (this._ioctl && this._ioctl.isWorking()) {
|
|
160
|
+
this._ioctl.pause();
|
|
161
|
+
this._disableStatisticsReporter();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
resume() {
|
|
166
|
+
if (this._ioctl && this._ioctl.isPaused()) {
|
|
167
|
+
this._ioctl.resume();
|
|
168
|
+
this._enableStatisticsReporter();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
switchAudioPid(pid) {
|
|
173
|
+
if (this._demuxer) {
|
|
174
|
+
if (typeof this._demuxer.setPreferredAudioPid === 'function') {
|
|
175
|
+
this._demuxer.setPreferredAudioPid(pid);
|
|
176
|
+
}
|
|
177
|
+
if (typeof this._demuxer.selectAudioPid === 'function') {
|
|
178
|
+
this._demuxer.selectAudioPid(pid);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
seek(milliseconds) {
|
|
184
|
+
if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let targetSegmentIndex = this._searchSegmentIndexContains(milliseconds);
|
|
189
|
+
|
|
190
|
+
if (targetSegmentIndex === this._currentSegmentIndex) {
|
|
191
|
+
// intra-segment seeking
|
|
192
|
+
let segmentInfo = this._mediaInfo.segments[targetSegmentIndex];
|
|
193
|
+
|
|
194
|
+
if (segmentInfo == undefined) {
|
|
195
|
+
// current segment loading started, but mediainfo hasn't received yet
|
|
196
|
+
// wait for the metadata loaded, then seek to expected position
|
|
197
|
+
this._pendingSeekTime = milliseconds;
|
|
198
|
+
} else {
|
|
199
|
+
let keyframe = segmentInfo.getNearestKeyframe(milliseconds);
|
|
200
|
+
this._remuxer.seek(keyframe.milliseconds);
|
|
201
|
+
this._ioctl.seek(keyframe.fileposition);
|
|
202
|
+
// Will be resolved in _onRemuxerMediaSegmentArrival()
|
|
203
|
+
this._pendingResolveSeekPoint = keyframe.milliseconds;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
// cross-segment seeking
|
|
207
|
+
let targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex];
|
|
208
|
+
|
|
209
|
+
if (targetSegmentInfo == undefined) {
|
|
210
|
+
// target segment hasn't been loaded. We need metadata then seek to expected time
|
|
211
|
+
this._pendingSeekTime = milliseconds;
|
|
212
|
+
this._internalAbort();
|
|
213
|
+
this._remuxer.seek();
|
|
214
|
+
this._remuxer.insertDiscontinuity();
|
|
215
|
+
this._loadSegment(targetSegmentIndex);
|
|
216
|
+
// Here we wait for the metadata loaded, then seek to expected position
|
|
217
|
+
} else {
|
|
218
|
+
// We have target segment's metadata, direct seek to target position
|
|
219
|
+
let keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds);
|
|
220
|
+
this._internalAbort();
|
|
221
|
+
this._remuxer.seek(milliseconds);
|
|
222
|
+
this._remuxer.insertDiscontinuity();
|
|
223
|
+
this._demuxer.resetMediaInfo();
|
|
224
|
+
this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase;
|
|
225
|
+
this._loadSegment(targetSegmentIndex, keyframe.fileposition);
|
|
226
|
+
this._pendingResolveSeekPoint = keyframe.milliseconds;
|
|
227
|
+
this._reportSegmentMediaInfo(targetSegmentIndex);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this._enableStatisticsReporter();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
_searchSegmentIndexContains(milliseconds) {
|
|
235
|
+
let segments = this._mediaDataSource.segments;
|
|
236
|
+
let idx = segments.length - 1;
|
|
237
|
+
|
|
238
|
+
for (let i = 0; i < segments.length; i++) {
|
|
239
|
+
if (milliseconds < segments[i].timestampBase) {
|
|
240
|
+
idx = i - 1;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return idx;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_onInitChunkArrival(data, byteStart) {
|
|
248
|
+
let consumed = 0;
|
|
249
|
+
|
|
250
|
+
if (byteStart > 0) {
|
|
251
|
+
// IOController seeked immediately after opened, byteStart > 0 callback may received
|
|
252
|
+
this._demuxer.bindDataSource(this._ioctl);
|
|
253
|
+
this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase;
|
|
254
|
+
|
|
255
|
+
consumed = this._demuxer.parseChunks(data, byteStart);
|
|
256
|
+
} else {
|
|
257
|
+
// byteStart == 0, Initial data, probe it first
|
|
258
|
+
let probeData = null;
|
|
259
|
+
|
|
260
|
+
// Try probing input data as FLV first
|
|
261
|
+
probeData = FLVDemuxer.probe(data);
|
|
262
|
+
if (probeData.match) {
|
|
263
|
+
// Hit as FLV
|
|
264
|
+
this._setupFLVDemuxerRemuxer(probeData);
|
|
265
|
+
consumed = this._demuxer.parseChunks(data, byteStart);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!probeData.match && !probeData.needMoreData) {
|
|
269
|
+
// Non-FLV, try MPEG-TS probe
|
|
270
|
+
probeData = TSDemuxer.probe(data);
|
|
271
|
+
if (probeData.match) {
|
|
272
|
+
// Hit as MPEG-TS
|
|
273
|
+
this._setupTSDemuxerRemuxer(probeData);
|
|
274
|
+
consumed = this._demuxer.parseChunks(data, byteStart);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!probeData.match && !probeData.needMoreData) {
|
|
279
|
+
// Both probing as FLV / MPEG-TS failed, report error
|
|
280
|
+
probeData = null;
|
|
281
|
+
Log.e(this.TAG, 'Non MPEG-TS/FLV, Unsupported media type!');
|
|
282
|
+
Promise.resolve().then(() => {
|
|
283
|
+
this._internalAbort();
|
|
284
|
+
});
|
|
285
|
+
this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, DemuxErrors.FORMAT_UNSUPPORTED, 'Non MPEG-TS/FLV, Unsupported media type!');
|
|
286
|
+
// Leave consumed as 0
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return consumed;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
_setupFLVDemuxerRemuxer(probeData) {
|
|
294
|
+
this._demuxer = new FLVDemuxer(probeData, this._config);
|
|
295
|
+
|
|
296
|
+
if (!this._remuxer) {
|
|
297
|
+
this._remuxer = new MP4Remuxer(this._config);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let mds = this._mediaDataSource;
|
|
301
|
+
if (mds.duration != undefined && !isNaN(mds.duration)) {
|
|
302
|
+
this._demuxer.overridedDuration = mds.duration;
|
|
303
|
+
}
|
|
304
|
+
if (typeof mds.hasAudio === 'boolean') {
|
|
305
|
+
this._demuxer.overridedHasAudio = mds.hasAudio;
|
|
306
|
+
}
|
|
307
|
+
if (typeof mds.hasVideo === 'boolean') {
|
|
308
|
+
this._demuxer.overridedHasVideo = mds.hasVideo;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase;
|
|
312
|
+
|
|
313
|
+
this._demuxer.onError = this._onDemuxException.bind(this);
|
|
314
|
+
this._demuxer.onMediaInfo = this._onMediaInfo.bind(this);
|
|
315
|
+
this._demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this);
|
|
316
|
+
this._demuxer.onScriptDataArrived = this._onScriptDataArrived.bind(this);
|
|
317
|
+
this._demuxer.onTracksUpdated = this._onTracksUpdated.bind(this);
|
|
318
|
+
this._demuxer.onSeiArrived = this._onSEI.bind(this);
|
|
319
|
+
|
|
320
|
+
this._remuxer.bindDataSource(this._demuxer
|
|
321
|
+
.bindDataSource(this._ioctl
|
|
322
|
+
));
|
|
323
|
+
|
|
324
|
+
this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this);
|
|
325
|
+
this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
_setupTSDemuxerRemuxer(probeData) {
|
|
329
|
+
let demuxer = this._demuxer = new TSDemuxer(probeData, this._config);
|
|
330
|
+
|
|
331
|
+
if (!this._remuxer) {
|
|
332
|
+
this._remuxer = new MP4Remuxer(this._config);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
demuxer.onError = this._onDemuxException.bind(this);
|
|
336
|
+
demuxer.onMediaInfo = this._onMediaInfo.bind(this);
|
|
337
|
+
demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this);
|
|
338
|
+
demuxer.onTimedID3Metadata = this._onTimedID3Metadata.bind(this);
|
|
339
|
+
demuxer.onPGSSubtitleData = this._onPGSSubtitle.bind(this);
|
|
340
|
+
demuxer.onSynchronousKLVMetadata = this._onSynchronousKLVMetadata.bind(this);
|
|
341
|
+
demuxer.onAsynchronousKLVMetadata = this._onAsynchronousKLVMetadata.bind(this);
|
|
342
|
+
demuxer.onSMPTE2038Metadata = this._onSMPTE2038Metadata.bind(this);
|
|
343
|
+
demuxer.onSEI = this._onSEI.bind(this);
|
|
344
|
+
demuxer.onSCTE35Metadata = this._onSCTE35Metadata.bind(this);
|
|
345
|
+
demuxer.onPESPrivateData = this._onPESPrivateData.bind(this);
|
|
346
|
+
demuxer.onPESPrivateDataDescriptor = this._onPESPrivateDataDescriptor.bind(this);
|
|
347
|
+
demuxer.onTracksUpdated = this._onTracksUpdated.bind(this);
|
|
348
|
+
demuxer.onPESPrivateData = this._onPESPrivateData.bind(this);
|
|
349
|
+
|
|
350
|
+
this._remuxer.bindDataSource(this._demuxer);
|
|
351
|
+
this._demuxer.bindDataSource(this._ioctl);
|
|
352
|
+
|
|
353
|
+
this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this);
|
|
354
|
+
this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
_onMediaInfo(mediaInfo) {
|
|
358
|
+
if (this._mediaInfo == null) {
|
|
359
|
+
// Store first segment's mediainfo as global mediaInfo
|
|
360
|
+
this._mediaInfo = Object.assign({}, mediaInfo);
|
|
361
|
+
this._mediaInfo.keyframesIndex = null;
|
|
362
|
+
this._mediaInfo.segments = [];
|
|
363
|
+
this._mediaInfo.segmentCount = this._mediaDataSource.segments.length;
|
|
364
|
+
Object.setPrototypeOf(this._mediaInfo, MediaInfo.prototype);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
let segmentInfo = Object.assign({}, mediaInfo);
|
|
368
|
+
Object.setPrototypeOf(segmentInfo, MediaInfo.prototype);
|
|
369
|
+
this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo;
|
|
370
|
+
|
|
371
|
+
// notify mediaInfo update
|
|
372
|
+
this._reportSegmentMediaInfo(this._currentSegmentIndex);
|
|
373
|
+
|
|
374
|
+
if (this._pendingSeekTime != null) {
|
|
375
|
+
Promise.resolve().then(() => {
|
|
376
|
+
let target = this._pendingSeekTime;
|
|
377
|
+
this._pendingSeekTime = null;
|
|
378
|
+
this.seek(target);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
_onMetaDataArrived(metadata) {
|
|
384
|
+
this._emitter.emit(TransmuxingEvents.METADATA_ARRIVED, metadata);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
_onScriptDataArrived(data) {
|
|
388
|
+
this._emitter.emit(TransmuxingEvents.SCRIPTDATA_ARRIVED, data);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
_onTracksUpdated(data) {
|
|
392
|
+
this._emitter.emit(TransmuxingEvents.TRACKS_UPDATED, data);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
_onTimedID3Metadata(timed_id3_metadata) {
|
|
396
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
397
|
+
if (timestamp_base == undefined) { return; }
|
|
398
|
+
|
|
399
|
+
if (timed_id3_metadata.pts != undefined) {
|
|
400
|
+
timed_id3_metadata.pts -= timestamp_base;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (timed_id3_metadata.dts != undefined) {
|
|
404
|
+
timed_id3_metadata.dts -= timestamp_base;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this._emitter.emit(TransmuxingEvents.TIMED_ID3_METADATA_ARRIVED, timed_id3_metadata);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
_onPGSSubtitle(pgs_data) {
|
|
411
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
412
|
+
if (timestamp_base == undefined) { return; }
|
|
413
|
+
|
|
414
|
+
if (pgs_data.pts != undefined) {
|
|
415
|
+
pgs_data.pts -= timestamp_base;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (pgs_data.dts != undefined) {
|
|
419
|
+
pgs_data.dts -= timestamp_base;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this._emitter.emit(TransmuxingEvents.PGS_SUBTITLE_ARRIVED, pgs_data);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
_onSynchronousKLVMetadata(synchronous_klv_metadata) {
|
|
426
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
427
|
+
if (timestamp_base == undefined) { return; }
|
|
428
|
+
|
|
429
|
+
if (synchronous_klv_metadata.pts != undefined) {
|
|
430
|
+
synchronous_klv_metadata.pts -= timestamp_base;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (synchronous_klv_metadata.dts != undefined) {
|
|
434
|
+
synchronous_klv_metadata.dts -= timestamp_base;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this._emitter.emit(TransmuxingEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED, synchronous_klv_metadata);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
_onAsynchronousKLVMetadata(asynchronous_klv_metadata) {
|
|
441
|
+
this._emitter.emit(TransmuxingEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED, asynchronous_klv_metadata);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
_onSMPTE2038Metadata(smpte2038_metadata) {
|
|
445
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
446
|
+
if (timestamp_base == undefined) { return; }
|
|
447
|
+
|
|
448
|
+
if (smpte2038_metadata.pts != undefined) {
|
|
449
|
+
smpte2038_metadata.pts -= timestamp_base;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (smpte2038_metadata.dts != undefined) {
|
|
453
|
+
smpte2038_metadata.dts -= timestamp_base;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (smpte2038_metadata.nearest_pts != undefined) {
|
|
457
|
+
smpte2038_metadata.nearest_pts -= timestamp_base;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this._emitter.emit(TransmuxingEvents.SMPTE2038_METADATA_ARRIVED, smpte2038_metadata);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
_onSEI(sei_data) {
|
|
464
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
465
|
+
if (timestamp_base == undefined) { return; }
|
|
466
|
+
|
|
467
|
+
if (sei_data.pts != undefined) {
|
|
468
|
+
sei_data.pts -= timestamp_base;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
this._emitter.emit(TransmuxingEvents.SEI_ARRIVED, sei_data);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
_onSCTE35Metadata(scte35) {
|
|
475
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
476
|
+
if (timestamp_base == undefined) { return; }
|
|
477
|
+
|
|
478
|
+
if (scte35.pts != undefined) {
|
|
479
|
+
scte35.pts -= timestamp_base;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (scte35.nearest_pts != undefined) {
|
|
483
|
+
scte35.nearest_pts -= timestamp_base;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
this._emitter.emit(TransmuxingEvents.SCTE35_METADATA_ARRIVED, scte35);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
_onPESPrivateDataDescriptor(descriptor) {
|
|
490
|
+
this._emitter.emit(TransmuxingEvents.PES_PRIVATE_DATA_DESCRIPTOR, descriptor);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_onPESPrivateData(private_data) {
|
|
494
|
+
let timestamp_base = this._remuxer.getTimestampBase();
|
|
495
|
+
if (timestamp_base == undefined) { return; }
|
|
496
|
+
|
|
497
|
+
if (private_data.pts != undefined) {
|
|
498
|
+
private_data.pts -= timestamp_base;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (private_data.nearest_pts != undefined) {
|
|
502
|
+
private_data.nearest_pts -= timestamp_base;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (private_data.dts != undefined) {
|
|
506
|
+
private_data.dts -= timestamp_base;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this._emitter.emit(TransmuxingEvents.PES_PRIVATE_DATA_ARRIVED, private_data);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
_onIOSeeked() {
|
|
513
|
+
this._remuxer.insertDiscontinuity();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
_onIOComplete(extraData) {
|
|
517
|
+
let segmentIndex = extraData;
|
|
518
|
+
let nextSegmentIndex = segmentIndex + 1;
|
|
519
|
+
|
|
520
|
+
if (nextSegmentIndex < this._mediaDataSource.segments.length) {
|
|
521
|
+
this._internalAbort();
|
|
522
|
+
if (this._remuxer) {
|
|
523
|
+
this._remuxer.flushStashedSamples();
|
|
524
|
+
}
|
|
525
|
+
this._loadSegment(nextSegmentIndex);
|
|
526
|
+
} else {
|
|
527
|
+
if (this._remuxer) {
|
|
528
|
+
this._remuxer.flushStashedSamples();
|
|
529
|
+
}
|
|
530
|
+
this._emitter.emit(TransmuxingEvents.LOADING_COMPLETE);
|
|
531
|
+
this._disableStatisticsReporter();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
_onIORedirect(redirectedURL) {
|
|
536
|
+
let segmentIndex = this._ioctl.extraData;
|
|
537
|
+
this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
_onIORecoveredEarlyEof() {
|
|
541
|
+
this._emitter.emit(TransmuxingEvents.RECOVERED_EARLY_EOF);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
_onIOException(type, info) {
|
|
545
|
+
Log.e(this.TAG, `IOException: type = ${type}, code = ${info.code}, msg = ${info.msg}`);
|
|
546
|
+
this._emitter.emit(TransmuxingEvents.IO_ERROR, type, info);
|
|
547
|
+
this._disableStatisticsReporter();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
_onDemuxException(type, info) {
|
|
551
|
+
Log.e(this.TAG, `DemuxException: type = ${type}, info = ${info}`);
|
|
552
|
+
this._emitter.emit(TransmuxingEvents.DEMUX_ERROR, type, info);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
_onRemuxerInitSegmentArrival(type, initSegment) {
|
|
556
|
+
this._emitter.emit(TransmuxingEvents.INIT_SEGMENT, type, initSegment);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
_onRemuxerMediaSegmentArrival(type, mediaSegment) {
|
|
560
|
+
if (this._pendingSeekTime != null) {
|
|
561
|
+
// Media segments after new-segment cross-seeking should be dropped.
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
this._emitter.emit(TransmuxingEvents.MEDIA_SEGMENT, type, mediaSegment);
|
|
565
|
+
|
|
566
|
+
// Resolve pending seekPoint
|
|
567
|
+
if (this._pendingResolveSeekPoint != null && type === 'video') {
|
|
568
|
+
let syncPoints = mediaSegment.info.syncPoints;
|
|
569
|
+
let seekpoint = this._pendingResolveSeekPoint;
|
|
570
|
+
this._pendingResolveSeekPoint = null;
|
|
571
|
+
|
|
572
|
+
// Safari: Pass PTS for recommend_seekpoint
|
|
573
|
+
if (Browser.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) {
|
|
574
|
+
seekpoint = syncPoints[0].pts;
|
|
575
|
+
}
|
|
576
|
+
// else: use original DTS (keyframe.milliseconds)
|
|
577
|
+
|
|
578
|
+
this._emitter.emit(TransmuxingEvents.RECOMMEND_SEEKPOINT, seekpoint);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
_enableStatisticsReporter() {
|
|
583
|
+
if (this._statisticsReporter == null) {
|
|
584
|
+
this._statisticsReporter = self.setInterval(
|
|
585
|
+
this._reportStatisticsInfo.bind(this),
|
|
586
|
+
this._config.statisticsInfoReportInterval);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
_disableStatisticsReporter() {
|
|
591
|
+
if (this._statisticsReporter) {
|
|
592
|
+
self.clearInterval(this._statisticsReporter);
|
|
593
|
+
this._statisticsReporter = null;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
_reportSegmentMediaInfo(segmentIndex) {
|
|
598
|
+
let segmentInfo = this._mediaInfo.segments[segmentIndex];
|
|
599
|
+
let exportInfo = Object.assign({}, segmentInfo);
|
|
600
|
+
|
|
601
|
+
exportInfo.duration = this._mediaInfo.duration;
|
|
602
|
+
exportInfo.segmentCount = this._mediaInfo.segmentCount;
|
|
603
|
+
delete exportInfo.segments;
|
|
604
|
+
delete exportInfo.keyframesIndex;
|
|
605
|
+
|
|
606
|
+
this._emitter.emit(TransmuxingEvents.MEDIA_INFO, exportInfo);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
_reportStatisticsInfo() {
|
|
610
|
+
let info = {};
|
|
611
|
+
|
|
612
|
+
info.url = this._ioctl.currentURL;
|
|
613
|
+
info.hasRedirect = this._ioctl.hasRedirect;
|
|
614
|
+
if (info.hasRedirect) {
|
|
615
|
+
info.redirectedURL = this._ioctl.currentRedirectedURL;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
info.speed = this._ioctl.currentSpeed;
|
|
619
|
+
info.loaderType = this._ioctl.loaderType;
|
|
620
|
+
info.currentSegmentIndex = this._currentSegmentIndex;
|
|
621
|
+
info.totalSegmentCount = this._mediaDataSource.segments.length;
|
|
622
|
+
|
|
623
|
+
this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, info);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export default TransmuxingController;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* @author zheng qian <xqq@xqq.im>
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
enum TransmuxingEvents {
|
|
20
|
+
IO_ERROR = 'io_error',
|
|
21
|
+
DEMUX_ERROR = 'demux_error',
|
|
22
|
+
INIT_SEGMENT = 'init_segment',
|
|
23
|
+
MEDIA_SEGMENT = 'media_segment',
|
|
24
|
+
LOADING_COMPLETE = 'loading_complete',
|
|
25
|
+
RECOVERED_EARLY_EOF = 'recovered_early_eof',
|
|
26
|
+
MEDIA_INFO = 'media_info',
|
|
27
|
+
METADATA_ARRIVED = 'metadata_arrived',
|
|
28
|
+
SCRIPTDATA_ARRIVED = 'scriptdata_arrived',
|
|
29
|
+
TRACKS_UPDATED = 'tracks_updated',
|
|
30
|
+
TIMED_ID3_METADATA_ARRIVED = 'timed_id3_metadata_arrived',
|
|
31
|
+
PGS_SUBTITLE_ARRIVED = 'pgs_subtitle_arrived',
|
|
32
|
+
SYNCHRONOUS_KLV_METADATA_ARRIVED = 'synchronous_klv_metadata_arrived',
|
|
33
|
+
ASYNCHRONOUS_KLV_METADATA_ARRIVED = 'asynchronous_klv_metadata_arrived',
|
|
34
|
+
SMPTE2038_METADATA_ARRIVED = 'smpte2038_metadata_arrived',
|
|
35
|
+
SEI_ARRIVED = 'sei_arrived',
|
|
36
|
+
SCTE35_METADATA_ARRIVED = 'scte35_metadata_arrived',
|
|
37
|
+
PES_PRIVATE_DATA_DESCRIPTOR = 'pes_private_data_descriptor',
|
|
38
|
+
PES_PRIVATE_DATA_ARRIVED = 'pes_private_data_arrived',
|
|
39
|
+
STATISTICS_INFO = 'statistics_info',
|
|
40
|
+
RECOMMEND_SEEKPOINT = 'recommend_seekpoint'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default TransmuxingEvents;
|