@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,479 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2023 zheng qian. 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 * as EventEmitter from 'events';
|
|
20
|
+
import * as work from '../utils/webworkify-webpack';
|
|
21
|
+
import type PlayerEngine from './player-engine';
|
|
22
|
+
import Log from '../utils/logger';
|
|
23
|
+
import LoggingControl from '../utils/logging-control.js';
|
|
24
|
+
import { createDefaultConfig } from '../config';
|
|
25
|
+
import MediaInfo from '../core/media-info';
|
|
26
|
+
import MSEEvents from '../core/mse-events';
|
|
27
|
+
import PlayerEvents from './player-events';
|
|
28
|
+
import TransmuxingEvents from '../core/transmuxing-events';
|
|
29
|
+
import SeekingHandler from './seeking-handler';
|
|
30
|
+
import LoadingController from './loading-controller';
|
|
31
|
+
import StartupStallJumper from './startup-stall-jumper';
|
|
32
|
+
import LiveLatencyChaser from './live-latency-chaser';
|
|
33
|
+
import LiveLatencySynchronizer from './live-latency-synchronizer';
|
|
34
|
+
import {
|
|
35
|
+
WorkerCommandPacket,
|
|
36
|
+
WorkerCommandPacketInit,
|
|
37
|
+
WorkerCommandPacketLoggingConfig,
|
|
38
|
+
WorkerCommandPacketTimeUpdate,
|
|
39
|
+
WorkerCommandPacketReadyStateChange,
|
|
40
|
+
WorkerCommandPacketUnbufferedSeek
|
|
41
|
+
} from './player-engine-worker-cmd-def.js';
|
|
42
|
+
import {
|
|
43
|
+
WorkerMessagePacket,
|
|
44
|
+
WorkerMessagePacketBufferedPositionChanged,
|
|
45
|
+
WorkerMessagePacketLogcatCallback,
|
|
46
|
+
WorkerMessagePacketMSEEvent,
|
|
47
|
+
WorkerMessagePacketMSEInit,
|
|
48
|
+
WorkerMessagePacketPlayerEvent,
|
|
49
|
+
WorkerMessagePacketPlayerEventError,
|
|
50
|
+
WorkerMessagePacketPlayerEventExtraData,
|
|
51
|
+
WorkerMessagePacketTransmuxingEvent,
|
|
52
|
+
WorkerMessagePacketTransmuxingEventInfo,
|
|
53
|
+
WorkerMessagePacketTransmuxingEventRecommendSeekpoint,
|
|
54
|
+
} from './player-engine-worker-msg-def.js';
|
|
55
|
+
|
|
56
|
+
class PlayerEngineDedicatedThread implements PlayerEngine {
|
|
57
|
+
|
|
58
|
+
private readonly TAG: string = 'PlayerEngineDedicatedThread';
|
|
59
|
+
|
|
60
|
+
private _emitter: EventEmitter = new EventEmitter();
|
|
61
|
+
private _media_data_source: any;
|
|
62
|
+
private _config: any;
|
|
63
|
+
|
|
64
|
+
private _media_element?: HTMLMediaElement = null;
|
|
65
|
+
|
|
66
|
+
private _worker: Worker;
|
|
67
|
+
private _worker_destroying: boolean = false;
|
|
68
|
+
|
|
69
|
+
private _seeking_handler?: SeekingHandler = null;
|
|
70
|
+
private _loading_controller?: LoadingController = null;
|
|
71
|
+
private _startup_stall_jumper?: StartupStallJumper = null;
|
|
72
|
+
private _live_latency_chaser?: LiveLatencyChaser = null;
|
|
73
|
+
private _live_latency_synchronizer?: LiveLatencySynchronizer = null;
|
|
74
|
+
|
|
75
|
+
private _pending_seek_time?: number = null;
|
|
76
|
+
|
|
77
|
+
private _media_info?: MediaInfo = null;
|
|
78
|
+
private _statistics_info?: any = null;
|
|
79
|
+
|
|
80
|
+
private e?: any = null;
|
|
81
|
+
|
|
82
|
+
public static isSupported(): boolean {
|
|
83
|
+
if (!self.Worker) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (self.MediaSource &&
|
|
87
|
+
('canConstructInDedicatedWorker' in self.MediaSource) &&
|
|
88
|
+
(self.MediaSource['canConstructInDedicatedWorker'] === true)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if ((self as any).ManagedMediaSource &&
|
|
92
|
+
('canConstructInDedicatedWorker' in (self as any).ManagedMediaSource) &&
|
|
93
|
+
((self as any).ManagedMediaSource['canConstructInDedicatedWorker'] === true)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public constructor(mediaDataSource: any, config: any) {
|
|
100
|
+
this._media_data_source = mediaDataSource;
|
|
101
|
+
this._config = createDefaultConfig();
|
|
102
|
+
|
|
103
|
+
if (typeof config === 'object') {
|
|
104
|
+
Object.assign(this._config, config);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (mediaDataSource.isLive === true) {
|
|
108
|
+
this._config.isLive = true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.e = {
|
|
112
|
+
onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this),
|
|
113
|
+
onMediaLoadedMetadata: this._onMediaLoadedMetadata.bind(this),
|
|
114
|
+
onMediaTimeUpdate: this._onMediaTimeUpdate.bind(this),
|
|
115
|
+
onMediaReadyStateChanged: this._onMediaReadyStateChange.bind(this),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
LoggingControl.registerListener(this.e.onLoggingConfigChanged);
|
|
119
|
+
|
|
120
|
+
this._worker = work(require.resolve('./player-engine-worker'), {all: true}) as Worker;
|
|
121
|
+
this._worker.addEventListener('message', this._onWorkerMessage.bind(this));
|
|
122
|
+
|
|
123
|
+
this._worker.postMessage({
|
|
124
|
+
cmd: 'init',
|
|
125
|
+
media_data_source: this._media_data_source,
|
|
126
|
+
config: this._config
|
|
127
|
+
} as WorkerCommandPacketInit);
|
|
128
|
+
|
|
129
|
+
this._worker.postMessage({
|
|
130
|
+
cmd: 'logging_config',
|
|
131
|
+
logging_config: LoggingControl.getConfig()
|
|
132
|
+
} as WorkerCommandPacketLoggingConfig);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public destroy(): void {
|
|
136
|
+
this._emitter.emit(PlayerEvents.DESTROYING);
|
|
137
|
+
this.unload();
|
|
138
|
+
this.detachMediaElement();
|
|
139
|
+
|
|
140
|
+
this._worker_destroying = true;
|
|
141
|
+
this._worker.postMessage({
|
|
142
|
+
cmd: 'destroy'
|
|
143
|
+
} as WorkerCommandPacket);
|
|
144
|
+
|
|
145
|
+
LoggingControl.removeListener(this.e.onLoggingConfigChanged);
|
|
146
|
+
this.e = null;
|
|
147
|
+
this._media_data_source = null;
|
|
148
|
+
|
|
149
|
+
this._emitter.removeAllListeners();
|
|
150
|
+
this._emitter = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public on(event: string, listener: (...args: any[]) => void): void {
|
|
154
|
+
this._emitter.addListener(event, listener);
|
|
155
|
+
// For media_info / statistics_info event, trigger it immediately
|
|
156
|
+
if (event === PlayerEvents.MEDIA_INFO && this._media_info) {
|
|
157
|
+
Promise.resolve().then(() => this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo));
|
|
158
|
+
} else if (event == PlayerEvents.STATISTICS_INFO && this._statistics_info) {
|
|
159
|
+
Promise.resolve().then(() => this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public off(event: string, listener: (...args: any[]) => void): void {
|
|
164
|
+
this._emitter.removeListener(event, listener);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public attachMediaElement(mediaElement: HTMLMediaElement): void {
|
|
168
|
+
this._media_element = mediaElement;
|
|
169
|
+
|
|
170
|
+
// Remove src / srcObject of HTMLMediaElement for cleanup
|
|
171
|
+
this._media_element.src = '';
|
|
172
|
+
this._media_element.removeAttribute('src');
|
|
173
|
+
this._media_element.srcObject = null;
|
|
174
|
+
this._media_element.load();
|
|
175
|
+
|
|
176
|
+
this._media_element.addEventListener('loadedmetadata', this.e.onMediaLoadedMetadata);
|
|
177
|
+
this._media_element.addEventListener('timeupdate', this.e.onMediaTimeUpdate);
|
|
178
|
+
this._media_element.addEventListener('readystatechange', this.e.onMediaReadyStateChanged);
|
|
179
|
+
|
|
180
|
+
this._worker.postMessage({
|
|
181
|
+
cmd: 'initialize_mse',
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Then wait for 'mse_init' message from worker to receive MediaSource handle
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public detachMediaElement(): void {
|
|
188
|
+
this._worker.postMessage({
|
|
189
|
+
cmd: 'shutdown_mse',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (this._media_element) {
|
|
193
|
+
// Remove all appended event listeners
|
|
194
|
+
this._media_element.removeEventListener('loadedmetadata', this.e.onMediaLoadedMetadata);
|
|
195
|
+
this._media_element.removeEventListener('timeupdate', this.e.onMediaTimeUpdate);
|
|
196
|
+
this._media_element.removeEventListener('readystatechange', this.e.onMediaReadyStateChanged);
|
|
197
|
+
|
|
198
|
+
// Detach media source from media element
|
|
199
|
+
this._media_element.src = '';
|
|
200
|
+
this._media_element.removeAttribute('src');
|
|
201
|
+
this._media_element.srcObject = null;
|
|
202
|
+
this._media_element.load();
|
|
203
|
+
this._media_element = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public load(): void {
|
|
208
|
+
this._worker.postMessage({
|
|
209
|
+
cmd: 'load',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
this._seeking_handler = new SeekingHandler(
|
|
213
|
+
this._config,
|
|
214
|
+
this._media_element,
|
|
215
|
+
this._onRequiredUnbufferedSeek.bind(this)
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
this._loading_controller = new LoadingController(
|
|
219
|
+
this._config,
|
|
220
|
+
this._media_element,
|
|
221
|
+
this._onRequestPauseTransmuxer.bind(this),
|
|
222
|
+
this._onRequestResumeTransmuxer.bind(this)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
this._startup_stall_jumper = new StartupStallJumper(
|
|
226
|
+
this._media_element,
|
|
227
|
+
this._onRequestDirectSeek.bind(this)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (this._config.isLive && this._config.liveBufferLatencyChasing) {
|
|
231
|
+
this._live_latency_chaser = new LiveLatencyChaser(
|
|
232
|
+
this._config,
|
|
233
|
+
this._media_element,
|
|
234
|
+
this._onRequestDirectSeek.bind(this)
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this._config.isLive && this._config.liveSync) {
|
|
239
|
+
this._live_latency_synchronizer = new LiveLatencySynchronizer(
|
|
240
|
+
this._config,
|
|
241
|
+
this._media_element
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Reset currentTime to 0
|
|
246
|
+
if (this._media_element.readyState > 0) {
|
|
247
|
+
// IE11 may throw InvalidStateError if readyState === 0
|
|
248
|
+
this._seeking_handler.directSeek(0);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public unload(): void {
|
|
253
|
+
this._media_element?.pause();
|
|
254
|
+
|
|
255
|
+
this._worker.postMessage({
|
|
256
|
+
cmd: 'unload',
|
|
257
|
+
} as WorkerCommandPacket);
|
|
258
|
+
|
|
259
|
+
this._live_latency_synchronizer?.destroy();
|
|
260
|
+
this._live_latency_synchronizer = null;
|
|
261
|
+
|
|
262
|
+
this._live_latency_chaser?.destroy();
|
|
263
|
+
this._live_latency_chaser = null;
|
|
264
|
+
|
|
265
|
+
this._startup_stall_jumper?.destroy();
|
|
266
|
+
this._startup_stall_jumper = null;
|
|
267
|
+
|
|
268
|
+
this._loading_controller?.destroy();
|
|
269
|
+
this._loading_controller = null;
|
|
270
|
+
|
|
271
|
+
this._seeking_handler?.destroy();
|
|
272
|
+
this._seeking_handler = null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public play(): Promise<void> {
|
|
276
|
+
return this._media_element.play();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
public pause(): void {
|
|
280
|
+
this._media_element.pause();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
public seek(seconds: number): void {
|
|
284
|
+
if (this._media_element && this._seeking_handler) {
|
|
285
|
+
this._seeking_handler.seek(seconds);
|
|
286
|
+
} else {
|
|
287
|
+
this._pending_seek_time = seconds;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public get mediaInfo(): MediaInfo {
|
|
292
|
+
return Object.assign({}, this._media_info);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public get statisticsInfo(): any {
|
|
296
|
+
return Object.assign({}, this._statistics_info);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public _onLoggingConfigChanged(config: any): void {
|
|
300
|
+
this._worker?.postMessage({
|
|
301
|
+
cmd: 'logging_config',
|
|
302
|
+
logging_config: config,
|
|
303
|
+
} as WorkerCommandPacketLoggingConfig);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private _onMSEUpdateEnd(): void {
|
|
307
|
+
if (this._config.isLive && this._config.liveBufferLatencyChasing && this._live_latency_chaser) {
|
|
308
|
+
this._live_latency_chaser.notifyBufferedRangeUpdate();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this._loading_controller.notifyBufferedPositionChanged();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private _onMSEBufferFull(): void {
|
|
315
|
+
Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task');
|
|
316
|
+
this._loading_controller.suspendTransmuxer();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private _onMediaLoadedMetadata(e: any): void {
|
|
320
|
+
if (this._pending_seek_time != null) {
|
|
321
|
+
this._seeking_handler.seek(this._pending_seek_time);
|
|
322
|
+
this._pending_seek_time = null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private _onRequestDirectSeek(target: number): void {
|
|
327
|
+
this._seeking_handler.directSeek(target);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private _onRequiredUnbufferedSeek(milliseconds: number): void {
|
|
331
|
+
this._worker.postMessage({
|
|
332
|
+
cmd: 'unbuffered_seek',
|
|
333
|
+
milliseconds: milliseconds
|
|
334
|
+
} as WorkerCommandPacketUnbufferedSeek);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private _onRequestPauseTransmuxer(): void {
|
|
338
|
+
this._worker.postMessage({
|
|
339
|
+
cmd: 'pause_transmuxer'
|
|
340
|
+
} as WorkerCommandPacket);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private _onRequestResumeTransmuxer(): void {
|
|
344
|
+
this._worker.postMessage({
|
|
345
|
+
cmd: 'resume_transmuxer'
|
|
346
|
+
} as WorkerCommandPacket);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private _onMediaTimeUpdate(e: any): void {
|
|
350
|
+
this._worker.postMessage({
|
|
351
|
+
cmd: 'timeupdate',
|
|
352
|
+
current_time: e.target.currentTime,
|
|
353
|
+
} as WorkerCommandPacketTimeUpdate);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private _onMediaReadyStateChange(e: any): void {
|
|
357
|
+
this._worker.postMessage({
|
|
358
|
+
cmd: 'readystatechange',
|
|
359
|
+
ready_state: e.target.readyState,
|
|
360
|
+
} as WorkerCommandPacketReadyStateChange);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private _onWorkerMessage(e: MessageEvent): void {
|
|
364
|
+
const message_packet = e.data as WorkerMessagePacket;
|
|
365
|
+
const msg = message_packet.msg;
|
|
366
|
+
|
|
367
|
+
if (msg == 'destroyed' || this._worker_destroying) {
|
|
368
|
+
this._worker_destroying = false;
|
|
369
|
+
this._worker?.terminate();
|
|
370
|
+
this._worker = null;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
switch (msg) {
|
|
375
|
+
case 'mse_init': {
|
|
376
|
+
const packet = message_packet as WorkerMessagePacketMSEInit;
|
|
377
|
+
// Use ManagedMediaSource only if w3c MediaSource is not available (e.g. iOS Safari)
|
|
378
|
+
const use_managed_media_source = ('ManagedMediaSource' in self) && !('MediaSource' in self);
|
|
379
|
+
if (use_managed_media_source) {
|
|
380
|
+
// When using ManagedMediaSource, MediaSource will not open unless disableRemotePlayback is set to true
|
|
381
|
+
this._media_element['disableRemotePlayback'] = true;
|
|
382
|
+
}
|
|
383
|
+
// Attach to HTMLMediaElement by using MediaSource Handle
|
|
384
|
+
this._media_element.srcObject = packet.handle;
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'mse_event': {
|
|
388
|
+
const packet = message_packet as WorkerMessagePacketMSEEvent;
|
|
389
|
+
if (packet.event == MSEEvents.UPDATE_END) {
|
|
390
|
+
this._onMSEUpdateEnd();
|
|
391
|
+
} else if (packet.event == MSEEvents.BUFFER_FULL) {
|
|
392
|
+
this._onMSEBufferFull();
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case 'transmuxing_event': {
|
|
397
|
+
const packet = message_packet as WorkerMessagePacketTransmuxingEvent;
|
|
398
|
+
if (packet.event == TransmuxingEvents.MEDIA_INFO) {
|
|
399
|
+
const packet = message_packet as WorkerMessagePacketTransmuxingEventInfo;
|
|
400
|
+
this._media_info = packet.info;
|
|
401
|
+
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, packet.info));
|
|
402
|
+
} else if (packet.event == TransmuxingEvents.STATISTICS_INFO) {
|
|
403
|
+
const packet = message_packet as WorkerMessagePacketTransmuxingEventInfo;
|
|
404
|
+
this._statistics_info = this._fillStatisticsInfo(packet.info);
|
|
405
|
+
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, packet.info));
|
|
406
|
+
} else if (packet.event == TransmuxingEvents.RECOMMEND_SEEKPOINT) {
|
|
407
|
+
const packet = message_packet as WorkerMessagePacketTransmuxingEventRecommendSeekpoint;
|
|
408
|
+
if (this._media_element && !this._config.accurateSeek) {
|
|
409
|
+
this._seeking_handler.directSeek(packet.milliseconds / 1000);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
case 'player_event': {
|
|
415
|
+
const packet = message_packet as WorkerMessagePacketPlayerEvent;
|
|
416
|
+
if (packet.event == PlayerEvents.ERROR) {
|
|
417
|
+
const packet = message_packet as WorkerMessagePacketPlayerEventError;
|
|
418
|
+
this._emitter.emit(PlayerEvents.ERROR, packet.error_type, packet.error_detail, packet.info);
|
|
419
|
+
} else if ('extraData' in packet) {
|
|
420
|
+
const packet = message_packet as WorkerMessagePacketPlayerEventExtraData;
|
|
421
|
+
this._emitter.emit(packet.event, packet.extraData);
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case 'logcat_callback': {
|
|
426
|
+
const packet = message_packet as WorkerMessagePacketLogcatCallback;
|
|
427
|
+
Log.emitter.emit('log', packet.type, packet.logcat);
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
case 'buffered_position_changed': {
|
|
431
|
+
const packet = message_packet as WorkerMessagePacketBufferedPositionChanged;
|
|
432
|
+
this._loading_controller.notifyBufferedPositionChanged(packet.buffered_position_milliseconds / 1000);
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private _fillStatisticsInfo(stat_info: any): any {
|
|
439
|
+
stat_info.playerType = 'MSEPlayer';
|
|
440
|
+
|
|
441
|
+
if (!(this._media_element instanceof HTMLVideoElement)) {
|
|
442
|
+
return stat_info;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let has_quality_info = true;
|
|
446
|
+
let decoded = 0;
|
|
447
|
+
let dropped = 0;
|
|
448
|
+
|
|
449
|
+
if (this._media_element.getVideoPlaybackQuality) {
|
|
450
|
+
const quality = this._media_element.getVideoPlaybackQuality();
|
|
451
|
+
decoded = quality.totalVideoFrames;
|
|
452
|
+
dropped = quality.droppedVideoFrames;
|
|
453
|
+
} else if (this._media_element['webkitDecodedFrameCount'] != undefined) {
|
|
454
|
+
decoded = this._media_element['webkitDecodedFrameCount'];
|
|
455
|
+
dropped = this._media_element['webkitDroppedFrameCount'];
|
|
456
|
+
} else {
|
|
457
|
+
has_quality_info = false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (has_quality_info) {
|
|
461
|
+
stat_info.decodedFrames = decoded;
|
|
462
|
+
stat_info.droppedFrames = dropped;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return stat_info;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
public switchAudioPid(pid: number): void {
|
|
469
|
+
if (this._worker && !this._worker_destroying) {
|
|
470
|
+
this._worker.postMessage({
|
|
471
|
+
cmd: 'switch_audio_pid',
|
|
472
|
+
pid: pid
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export default PlayerEngineDedicatedThread;
|