@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.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +158 -0
  3. package/README_ja.md +153 -0
  4. package/README_zh.md +157 -0
  5. package/d.ts/mpegts.d.ts +524 -0
  6. package/d.ts/src/core/mse-events.d.ts +9 -0
  7. package/d.ts/src/core/transmuxing-events.d.ts +24 -0
  8. package/d.ts/src/demux/aac.d.ts +44 -0
  9. package/d.ts/src/demux/ac3.d.ts +70 -0
  10. package/d.ts/src/demux/av1-parser.d.ts +77 -0
  11. package/d.ts/src/demux/av1.d.ts +11 -0
  12. package/d.ts/src/demux/base-demuxer.d.ts +55 -0
  13. package/d.ts/src/demux/h264.d.ts +40 -0
  14. package/d.ts/src/demux/h265.d.ts +65 -0
  15. package/d.ts/src/demux/klv.d.ts +17 -0
  16. package/d.ts/src/demux/mp3.d.ts +6 -0
  17. package/d.ts/src/demux/mpeg4-audio.d.ts +28 -0
  18. package/d.ts/src/demux/pat-pmt-pes.d.ts +106 -0
  19. package/d.ts/src/demux/patpmt.d.ts +40 -0
  20. package/d.ts/src/demux/pes-private-data.d.ts +14 -0
  21. package/d.ts/src/demux/pgs-data.d.ts +9 -0
  22. package/d.ts/src/demux/scte35.d.ts +250 -0
  23. package/d.ts/src/demux/sei.d.ts +8 -0
  24. package/d.ts/src/demux/smpte2038.d.ts +22 -0
  25. package/d.ts/src/demux/ts-demuxer.d.ts +124 -0
  26. package/d.ts/src/player/live-latency-chaser.d.ts +10 -0
  27. package/d.ts/src/player/live-latency-synchronizer.d.ts +10 -0
  28. package/d.ts/src/player/loading-controller.d.ts +19 -0
  29. package/d.ts/src/player/mse-player.d.ts +30 -0
  30. package/d.ts/src/player/player-engine-dedicated-thread-worker.d.ts +2 -0
  31. package/d.ts/src/player/player-engine-dedicated-thread.d.ts +48 -0
  32. package/d.ts/src/player/player-engine-main-thread.d.ts +50 -0
  33. package/d.ts/src/player/player-engine-worker-cmd-def.d.ts +25 -0
  34. package/d.ts/src/player/player-engine-worker-msg-def.d.ts +54 -0
  35. package/d.ts/src/player/player-engine-worker.d.ts +2 -0
  36. package/d.ts/src/player/player-engine.d.ts +16 -0
  37. package/d.ts/src/player/player-events.d.ts +21 -0
  38. package/d.ts/src/player/seeking-handler.d.ts +22 -0
  39. package/d.ts/src/player/startup-stall-jumper.d.ts +14 -0
  40. package/d.ts/src/utils/typedarray-equality.d.ts +2 -0
  41. package/dist/mpegts.js +3 -0
  42. package/dist/mpegts.js.LICENSE.txt +7 -0
  43. package/dist/mpegts.js.map +1 -0
  44. package/package.json +53 -0
  45. package/src/config.js +67 -0
  46. package/src/core/features.js +88 -0
  47. package/src/core/media-info.js +127 -0
  48. package/src/core/media-segment-info.js +230 -0
  49. package/src/core/mse-controller.js +599 -0
  50. package/src/core/mse-events.ts +28 -0
  51. package/src/core/transmuxer.js +346 -0
  52. package/src/core/transmuxing-controller.js +628 -0
  53. package/src/core/transmuxing-events.ts +43 -0
  54. package/src/core/transmuxing-worker.js +286 -0
  55. package/src/demux/aac.ts +397 -0
  56. package/src/demux/ac3.ts +335 -0
  57. package/src/demux/amf-parser.js +243 -0
  58. package/src/demux/av1-parser.ts +629 -0
  59. package/src/demux/av1.ts +103 -0
  60. package/src/demux/base-demuxer.ts +69 -0
  61. package/src/demux/demux-errors.js +26 -0
  62. package/src/demux/exp-golomb.js +116 -0
  63. package/src/demux/flv-demuxer.js +1854 -0
  64. package/src/demux/h264.ts +187 -0
  65. package/src/demux/h265-parser.js +501 -0
  66. package/src/demux/h265.ts +214 -0
  67. package/src/demux/klv.ts +40 -0
  68. package/src/demux/mp3.ts +7 -0
  69. package/src/demux/mpeg4-audio.ts +45 -0
  70. package/src/demux/pat-pmt-pes.ts +132 -0
  71. package/src/demux/pes-private-data.ts +16 -0
  72. package/src/demux/pgs-data.ts +11 -0
  73. package/src/demux/scte35.ts +723 -0
  74. package/src/demux/sei.ts +99 -0
  75. package/src/demux/smpte2038.ts +89 -0
  76. package/src/demux/sps-parser.js +298 -0
  77. package/src/demux/ts-demuxer.ts +2405 -0
  78. package/src/index.js +4 -0
  79. package/src/io/fetch-stream-loader.js +266 -0
  80. package/src/io/io-controller.js +647 -0
  81. package/src/io/loader.js +134 -0
  82. package/src/io/param-seek-handler.js +85 -0
  83. package/src/io/range-seek-handler.js +52 -0
  84. package/src/io/speed-sampler.js +93 -0
  85. package/src/io/websocket-loader.js +151 -0
  86. package/src/io/xhr-moz-chunked-loader.js +211 -0
  87. package/src/io/xhr-msstream-loader.js +307 -0
  88. package/src/io/xhr-range-loader.js +366 -0
  89. package/src/mpegts.js +95 -0
  90. package/src/player/live-latency-chaser.ts +66 -0
  91. package/src/player/live-latency-synchronizer.ts +79 -0
  92. package/src/player/loading-controller.ts +142 -0
  93. package/src/player/mse-player.ts +150 -0
  94. package/src/player/native-player.js +262 -0
  95. package/src/player/player-engine-dedicated-thread.ts +479 -0
  96. package/src/player/player-engine-main-thread.ts +463 -0
  97. package/src/player/player-engine-worker-cmd-def.ts +62 -0
  98. package/src/player/player-engine-worker-msg-def.ts +102 -0
  99. package/src/player/player-engine-worker.ts +370 -0
  100. package/src/player/player-engine.ts +35 -0
  101. package/src/player/player-errors.js +39 -0
  102. package/src/player/player-events.ts +40 -0
  103. package/src/player/seeking-handler.ts +205 -0
  104. package/src/player/startup-stall-jumper.ts +86 -0
  105. package/src/remux/aac-silent.js +56 -0
  106. package/src/remux/mp4-generator.js +866 -0
  107. package/src/remux/mp4-remuxer.js +778 -0
  108. package/src/utils/browser.js +128 -0
  109. package/src/utils/exception.js +73 -0
  110. package/src/utils/logger.js +140 -0
  111. package/src/utils/logging-control.js +165 -0
  112. package/src/utils/polyfill.js +68 -0
  113. package/src/utils/typedarray-equality.ts +69 -0
  114. package/src/utils/utf8-conv.js +84 -0
  115. package/src/utils/webworkify-webpack.js +202 -0
  116. package/tsconfig.json +16 -0
  117. package/tslint.json +1 -0
  118. package/types/index.d.ts +3 -0
  119. package/types/test-flv.ts +8 -0
  120. package/types/tsconfig.json +24 -0
  121. package/webpack.config.js +55 -0
@@ -0,0 +1,463 @@
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 type PlayerEngine from './player-engine';
21
+ import Log from '../utils/logger';
22
+ import { createDefaultConfig } from '../config';
23
+ import MSEController from '../core/mse-controller';
24
+ import PlayerEvents from './player-events';
25
+ import Transmuxer from '../core/transmuxer';
26
+ import MediaInfo from '../core/media-info';
27
+ import MSEEvents from '../core/mse-events';
28
+ import { ErrorTypes, ErrorDetails } from './player-errors';
29
+ import { IllegalStateException } from '../utils/exception';
30
+ import TransmuxingEvents from '../core/transmuxing-events';
31
+ import SeekingHandler from './seeking-handler';
32
+ import LoadingController from './loading-controller';
33
+ import StartupStallJumper from './startup-stall-jumper';
34
+ import LiveLatencyChaser from './live-latency-chaser';
35
+ import LiveLatencySynchronizer from './live-latency-synchronizer';
36
+
37
+ class PlayerEngineMainThread implements PlayerEngine {
38
+
39
+ private readonly TAG: string = 'PlayerEngineMainThread';
40
+
41
+ private _emitter: EventEmitter = new EventEmitter();
42
+ private _media_data_source: any;
43
+ private _config: any;
44
+
45
+ private _media_element?: HTMLMediaElement = null;
46
+
47
+ private _mse_controller?: MSEController = null;
48
+ private _transmuxer?: Transmuxer = null;
49
+
50
+ private _pending_seek_time?: number = null;
51
+
52
+ private _seeking_handler?: SeekingHandler = null;
53
+ private _loading_controller?: LoadingController = null;
54
+ private _startup_stall_jumper?: StartupStallJumper = null;
55
+ private _live_latency_chaser?: LiveLatencyChaser = null;
56
+ private _live_latency_synchronizer?: LiveLatencySynchronizer = null;
57
+
58
+ private _mse_source_opened: boolean = false;
59
+ private _has_pending_load: boolean = false;
60
+ private _loaded_metadata_received: boolean = false;
61
+
62
+ private _media_info?: MediaInfo = null;
63
+ private _statistics_info?: any = null;
64
+
65
+ private e?: any = null;
66
+
67
+ public constructor(mediaDataSource: any, config: any) {
68
+ this._media_data_source = mediaDataSource;
69
+ this._config = createDefaultConfig();
70
+
71
+ if (typeof config === 'object') {
72
+ Object.assign(this._config, config);
73
+ }
74
+
75
+ if (mediaDataSource.isLive === true) {
76
+ this._config.isLive = true;
77
+ }
78
+
79
+ this.e = {
80
+ onMediaLoadedMetadata: this._onMediaLoadedMetadata.bind(this),
81
+ };
82
+ }
83
+
84
+ public destroy(): void {
85
+ this._emitter.emit(PlayerEvents.DESTROYING);
86
+ if (this._transmuxer) {
87
+ this.unload();
88
+ }
89
+ if (this._media_element) {
90
+ this.detachMediaElement();
91
+ }
92
+ this.e = null;
93
+ this._media_data_source = null;
94
+
95
+ this._emitter.removeAllListeners();
96
+ this._emitter = null;
97
+ }
98
+
99
+ public on(event: string, listener: (...args: any[]) => void): void {
100
+ this._emitter.addListener(event, listener);
101
+ // For media_info / statistics_info event, trigger it immediately
102
+ if (event === PlayerEvents.MEDIA_INFO && this._media_info) {
103
+ Promise.resolve().then(() => this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo));
104
+ } else if (event == PlayerEvents.STATISTICS_INFO && this._statistics_info) {
105
+ Promise.resolve().then(() => this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo));
106
+ }
107
+ }
108
+
109
+ public off(event: string, listener: (...args: any[]) => void): void {
110
+ this._emitter.removeListener(event, listener);
111
+ }
112
+
113
+ public attachMediaElement(mediaElement: HTMLMediaElement): void {
114
+ this._media_element = mediaElement;
115
+
116
+ // Remove src / srcObject of HTMLMediaElement for cleanup
117
+ mediaElement.src = '';
118
+ mediaElement.removeAttribute('src');
119
+ mediaElement.srcObject = null;
120
+ mediaElement.load();
121
+
122
+ mediaElement.addEventListener('loadedmetadata', this.e.onMediaLoadedMetadata);
123
+
124
+ this._mse_controller = new MSEController(this._config);
125
+ this._mse_controller.on(MSEEvents.UPDATE_END, this._onMSEUpdateEnd.bind(this));
126
+ this._mse_controller.on(MSEEvents.BUFFER_FULL, this._onMSEBufferFull.bind(this));
127
+ this._mse_controller.on(MSEEvents.SOURCE_OPEN, this._onMSESourceOpen.bind(this));
128
+ this._mse_controller.on(MSEEvents.ERROR, this._onMSEError.bind(this));
129
+ this._mse_controller.on(MSEEvents.START_STREAMING, this._onMSEStartStreaming.bind(this));
130
+ this._mse_controller.on(MSEEvents.END_STREAMING, this._onMSEEndStreaming.bind(this));
131
+
132
+ this._mse_controller.initialize({
133
+ getCurrentTime: () => this._media_element.currentTime,
134
+ getReadyState: () => this._media_element.readyState,
135
+ });
136
+
137
+ // Attach media source into media element
138
+ if (this._mse_controller.isManagedMediaSource()) {
139
+ // Apple ManagedMediaSource
140
+ mediaElement['disableRemotePlayback'] = true;
141
+ mediaElement.srcObject = this._mse_controller.getObject();
142
+ } else {
143
+ // w3c MediaSource
144
+ mediaElement.src = this._mse_controller.getObjectURL();
145
+ }
146
+ }
147
+
148
+ public detachMediaElement(): void {
149
+ if (this._media_element) {
150
+ this._mse_controller.shutdown();
151
+
152
+ // Remove all appended event listeners
153
+ this._media_element.removeEventListener('loadedmetadata', this.e.onMediaLoadedMetadata);
154
+
155
+ // Detach media source from media element
156
+ this._media_element.src = '';
157
+ this._media_element.removeAttribute('src');
158
+ this._media_element.srcObject = null;
159
+ this._media_element.load();
160
+ this._media_element = null;
161
+
162
+ this._mse_controller.revokeObjectURL();
163
+ }
164
+ if (this._mse_controller) {
165
+ this._mse_controller.destroy();
166
+ this._mse_controller = null;
167
+ }
168
+ }
169
+
170
+ public load(): void {
171
+ if (!this._media_element) {
172
+ throw new IllegalStateException('HTMLMediaElement must be attached before load()!');
173
+ }
174
+ if (this._transmuxer) {
175
+ throw new IllegalStateException('load() has been called, please call unload() first!');
176
+ }
177
+ if (this._has_pending_load) {
178
+ // Defer load operation until MSE source open
179
+ return;
180
+ }
181
+
182
+ if (this._config.deferLoadAfterSourceOpen && !this._mse_source_opened) {
183
+ this._has_pending_load = true;
184
+ return;
185
+ }
186
+
187
+ this._transmuxer = new Transmuxer(this._media_data_source, this._config);
188
+
189
+ this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type: string, is: any) => {
190
+ this._mse_controller.appendInitSegment(is);
191
+ });
192
+ this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type: string, ms: any) => {
193
+ this._mse_controller.appendMediaSegment(ms);
194
+ if (!this._config.isLive && type === 'video' && ms.data && ms.data.byteLength > 0 && ('info' in ms)) {
195
+ this._seeking_handler.appendSyncPoints(ms.info.syncPoints);
196
+ }
197
+ this._loading_controller.notifyBufferedPositionChanged(ms.info.endDts / 1000);
198
+ });
199
+ this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => {
200
+ this._mse_controller.endOfStream();
201
+ this._emitter.emit(PlayerEvents.LOADING_COMPLETE);
202
+ });
203
+ this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => {
204
+ this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF);
205
+ });
206
+ this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail: any, info: any) => {
207
+ this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info);
208
+ });
209
+ this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail: any, info: any) => {
210
+ this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, info);
211
+ });
212
+ this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo: MediaInfo) => {
213
+ this._media_info = mediaInfo;
214
+ this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo));
215
+ });
216
+ this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo: any) => {
217
+ this._statistics_info = this._fillStatisticsInfo(statInfo);
218
+ this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, statInfo));
219
+ });
220
+ this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds: number) => {
221
+ if (this._media_element && !this._config.accurateSeek) {
222
+ this._seeking_handler.directSeek(milliseconds / 1000);
223
+ }
224
+ });
225
+ this._transmuxer.on(TransmuxingEvents.METADATA_ARRIVED, (metadata: any) => {
226
+ this._emitter.emit(PlayerEvents.METADATA_ARRIVED, metadata);
227
+ });
228
+ this._transmuxer.on(TransmuxingEvents.SCRIPTDATA_ARRIVED, (data: any) => {
229
+ this._emitter.emit(PlayerEvents.SCRIPTDATA_ARRIVED, data);
230
+ });
231
+ this._transmuxer.on((TransmuxingEvents as any).TRACKS_UPDATED, (data: any) => {
232
+ this._emitter.emit((PlayerEvents as any).TRACKS_UPDATED, data);
233
+ });
234
+ this._transmuxer.on(TransmuxingEvents.TIMED_ID3_METADATA_ARRIVED, (timed_id3_metadata: any) => {
235
+ this._emitter.emit(PlayerEvents.TIMED_ID3_METADATA_ARRIVED, timed_id3_metadata);
236
+ });
237
+ this._transmuxer.on(TransmuxingEvents.PGS_SUBTITLE_ARRIVED, (pgs_data: any) => {
238
+ this._emitter.emit(PlayerEvents.PGS_SUBTITLE_ARRIVED, pgs_data);
239
+ });
240
+ this._transmuxer.on(TransmuxingEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED, (synchronous_klv_metadata: any) => {
241
+ this._emitter.emit(PlayerEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED, synchronous_klv_metadata);
242
+ });
243
+ this._transmuxer.on(TransmuxingEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED, (asynchronous_klv_metadata: any) => {
244
+ this._emitter.emit(PlayerEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED, asynchronous_klv_metadata);
245
+ });
246
+ this._transmuxer.on(TransmuxingEvents.SMPTE2038_METADATA_ARRIVED, (smpte2038_metadata: any) => {
247
+ this._emitter.emit(PlayerEvents.SMPTE2038_METADATA_ARRIVED, smpte2038_metadata);
248
+ });
249
+ this._transmuxer.on(TransmuxingEvents.SEI_ARRIVED, (sei_data: any) => {
250
+ this._emitter.emit(PlayerEvents.SEI_ARRIVED, sei_data);
251
+ });
252
+ this._transmuxer.on(TransmuxingEvents.SCTE35_METADATA_ARRIVED, (scte35_metadata: any) => {
253
+ this._emitter.emit(PlayerEvents.SCTE35_METADATA_ARRIVED, scte35_metadata);
254
+ });
255
+ this._transmuxer.on(TransmuxingEvents.PES_PRIVATE_DATA_DESCRIPTOR, (descriptor: any) => {
256
+ this._emitter.emit(PlayerEvents.PES_PRIVATE_DATA_DESCRIPTOR, descriptor);
257
+ });
258
+ this._transmuxer.on(TransmuxingEvents.PES_PRIVATE_DATA_ARRIVED, (private_data: any) => {
259
+ this._emitter.emit(PlayerEvents.PES_PRIVATE_DATA_ARRIVED, private_data);
260
+ });
261
+
262
+ this._seeking_handler = new SeekingHandler(
263
+ this._config,
264
+ this._media_element,
265
+ this._onRequiredUnbufferedSeek.bind(this)
266
+ );
267
+
268
+ this._loading_controller = new LoadingController(
269
+ this._config,
270
+ this._media_element,
271
+ this._onRequestPauseTransmuxer.bind(this),
272
+ this._onRequestResumeTransmuxer.bind(this)
273
+ );
274
+
275
+ this._startup_stall_jumper = new StartupStallJumper(
276
+ this._media_element,
277
+ this._onRequestDirectSeek.bind(this)
278
+ );
279
+
280
+ if (this._config.isLive && this._config.liveBufferLatencyChasing) {
281
+ this._live_latency_chaser = new LiveLatencyChaser(
282
+ this._config,
283
+ this._media_element,
284
+ this._onRequestDirectSeek.bind(this)
285
+ );
286
+ }
287
+
288
+ if (this._config.isLive && this._config.liveSync) {
289
+ this._live_latency_synchronizer = new LiveLatencySynchronizer(
290
+ this._config,
291
+ this._media_element
292
+ );
293
+ }
294
+
295
+ // Reset currentTime to 0
296
+ if (this._media_element.readyState > 0) {
297
+ // IE11 may throw InvalidStateError if readyState === 0
298
+ this._seeking_handler.directSeek(0);
299
+ }
300
+
301
+ this._transmuxer.open();
302
+ }
303
+
304
+ public unload(): void {
305
+ this._media_element?.pause();
306
+
307
+ this._live_latency_synchronizer?.destroy();
308
+ this._live_latency_synchronizer = null;
309
+
310
+ this._live_latency_chaser?.destroy();
311
+ this._live_latency_chaser = null;
312
+
313
+ this._startup_stall_jumper?.destroy();
314
+ this._startup_stall_jumper = null;
315
+
316
+ this._loading_controller?.destroy();
317
+ this._loading_controller = null;
318
+
319
+ this._seeking_handler?.destroy();
320
+ this._seeking_handler = null;
321
+
322
+ this._mse_controller?.flush();
323
+
324
+ this._transmuxer?.close();
325
+ this._transmuxer?.destroy();
326
+ this._transmuxer = null;
327
+ }
328
+
329
+ public play(): Promise<void> {
330
+ return this._media_element.play();
331
+ }
332
+
333
+ public pause(): void {
334
+ this._media_element.pause();
335
+ }
336
+
337
+ public seek(seconds: number): void {
338
+ if (this._media_element && this._seeking_handler) {
339
+ this._seeking_handler.seek(seconds);
340
+ } else {
341
+ this._pending_seek_time = seconds;
342
+ }
343
+ }
344
+
345
+ public get mediaInfo(): MediaInfo {
346
+ return Object.assign({}, this._media_info);
347
+ }
348
+
349
+ public get statisticsInfo(): any {
350
+ return Object.assign({}, this._statistics_info);
351
+ }
352
+
353
+ private _onMSESourceOpen(): void {
354
+ this._mse_source_opened = true;
355
+ if (this._has_pending_load) {
356
+ this._has_pending_load = false;
357
+ this.load();
358
+ }
359
+ }
360
+
361
+ private _onMSEUpdateEnd(): void {
362
+ if (this._config.isLive && this._config.liveBufferLatencyChasing && this._live_latency_chaser) {
363
+ this._live_latency_chaser.notifyBufferedRangeUpdate();
364
+ }
365
+
366
+ this._loading_controller.notifyBufferedPositionChanged();
367
+ }
368
+
369
+ private _onMSEBufferFull(): void {
370
+ Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task');
371
+ this._loading_controller.suspendTransmuxer();
372
+ }
373
+
374
+ private _onMSEError(info: any): void {
375
+ this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, ErrorDetails.MEDIA_MSE_ERROR, info);
376
+ }
377
+
378
+ private _onMSEStartStreaming(): void {
379
+ if (!this._loaded_metadata_received) {
380
+ // Ignore initial startstreaming event since we have started loading data
381
+ return;
382
+ }
383
+ if (this._config.isLive) {
384
+ // For live stream, we do not suspend / resume transmuxer
385
+ return;
386
+ }
387
+ Log.v(this.TAG, 'Resume transmuxing task due to ManagedMediaSource onStartStreaming');
388
+ this._loading_controller.resumeTransmuxer();
389
+ }
390
+
391
+ private _onMSEEndStreaming(): void {
392
+ if (this._config.isLive) {
393
+ // For live stream, we do not suspend / resume transmuxer
394
+ return;
395
+ }
396
+ Log.v(this.TAG, 'Suspend transmuxing task due to ManagedMediaSource onEndStreaming');
397
+ this._loading_controller.suspendTransmuxer();
398
+ }
399
+
400
+ private _onMediaLoadedMetadata(e: any): void {
401
+ this._loaded_metadata_received = true;
402
+ if (this._pending_seek_time != null) {
403
+ this._seeking_handler.seek(this._pending_seek_time);
404
+ this._pending_seek_time = null;
405
+ }
406
+ }
407
+
408
+ private _onRequestDirectSeek(target: number): void {
409
+ this._seeking_handler.directSeek(target);
410
+ }
411
+
412
+ private _onRequiredUnbufferedSeek(milliseconds: number): void {
413
+ this._mse_controller.flush();
414
+ this._transmuxer.seek(milliseconds);
415
+ }
416
+
417
+ private _onRequestPauseTransmuxer(): void {
418
+ this._transmuxer.pause();
419
+ }
420
+
421
+ private _onRequestResumeTransmuxer(): void {
422
+ this._transmuxer.resume();
423
+ }
424
+
425
+ private _fillStatisticsInfo(stat_info: any): any {
426
+ stat_info.playerType = 'MSEPlayer';
427
+
428
+ if (!(this._media_element instanceof HTMLVideoElement)) {
429
+ return stat_info;
430
+ }
431
+
432
+ let has_quality_info = true;
433
+ let decoded = 0;
434
+ let dropped = 0;
435
+
436
+ if (this._media_element.getVideoPlaybackQuality) {
437
+ const quality = this._media_element.getVideoPlaybackQuality();
438
+ decoded = quality.totalVideoFrames;
439
+ dropped = quality.droppedVideoFrames;
440
+ } else if (this._media_element['webkitDecodedFrameCount'] != undefined) {
441
+ decoded = this._media_element['webkitDecodedFrameCount'];
442
+ dropped = this._media_element['webkitDroppedFrameCount'];
443
+ } else {
444
+ has_quality_info = false;
445
+ }
446
+
447
+ if (has_quality_info) {
448
+ stat_info.decodedFrames = decoded;
449
+ stat_info.droppedFrames = dropped;
450
+ }
451
+
452
+ return stat_info;
453
+ }
454
+
455
+ public switchAudioPid(pid: number): void {
456
+ if (this._transmuxer) {
457
+ (this._transmuxer as any).switchAudioPid(pid);
458
+ }
459
+ }
460
+
461
+ }
462
+
463
+ export default PlayerEngineMainThread;
@@ -0,0 +1,62 @@
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
+ export type WorkerCommandOp =
20
+ | 'logging_config'
21
+ | 'init'
22
+ | 'destroy'
23
+ | 'initialize_mse'
24
+ | 'shutdown_mse'
25
+ | 'load'
26
+ | 'unload'
27
+ | 'unbuffered_seek'
28
+ | 'timeupdate'
29
+ | 'readystatechange'
30
+ | 'pause_transmuxer'
31
+ | 'resume_transmuxer'
32
+ | 'switch_audio_pid';
33
+
34
+ export type WorkerCommandPacket = {
35
+ cmd: WorkerCommandOp,
36
+ };
37
+
38
+ export type WorkerCommandPacketInit = WorkerCommandPacket & {
39
+ cmd: 'init',
40
+ media_data_source: any,
41
+ config: any,
42
+ };
43
+
44
+ export type WorkerCommandPacketLoggingConfig = WorkerCommandPacket & {
45
+ cmd: 'logging_config',
46
+ logging_config: any,
47
+ };
48
+
49
+ export type WorkerCommandPacketUnbufferedSeek = WorkerCommandPacket & {
50
+ cmd: 'unbuffered_seek',
51
+ milliseconds: number,
52
+ };
53
+
54
+ export type WorkerCommandPacketTimeUpdate = WorkerCommandPacket & {
55
+ cmd: 'timeupdate',
56
+ current_time: number,
57
+ };
58
+
59
+ export type WorkerCommandPacketReadyStateChange = WorkerCommandPacket & {
60
+ cmd: 'readystatechange',
61
+ ready_state: number,
62
+ };
@@ -0,0 +1,102 @@
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 MSEEvents from '../core/mse-events';
20
+ import PlayerEvents from './player-events';
21
+ import TransmuxingEvents from '../core/transmuxing-events';
22
+
23
+ export type WorkerMessageType =
24
+ | 'destroyed'
25
+ | 'mse_init'
26
+ | 'mse_event'
27
+ | 'player_event'
28
+ | 'transmuxing_event'
29
+ | 'buffered_position_changed'
30
+ | 'logcat_callback';
31
+
32
+ export type WorkerMessagePacket = {
33
+ msg: WorkerMessageType,
34
+ };
35
+
36
+ export type WorkerMessagePacketMSEInit = WorkerMessagePacket & {
37
+ msg: 'mse_init',
38
+ handle: any,
39
+ };
40
+
41
+ export type WorkerMessagePacketMSEEvent = WorkerMessagePacket & {
42
+ msg: 'mse_event',
43
+ event: MSEEvents,
44
+ };
45
+
46
+ export type WorkerMessagePacketPlayerEvent = WorkerMessagePacket & {
47
+ msg: 'player_event',
48
+ event: PlayerEvents,
49
+ };
50
+
51
+ export type WorkerMessagePacketPlayerEventError = WorkerMessagePacketPlayerEvent & {
52
+ msg: 'player_event',
53
+ event: PlayerEvents.ERROR,
54
+ error_type: string,
55
+ error_detail: string,
56
+ info: any,
57
+ };
58
+
59
+ export type WorkerMessagePacketPlayerEventExtraData = WorkerMessagePacketPlayerEvent & {
60
+ msg: 'player_event',
61
+ event:
62
+ | PlayerEvents.METADATA_ARRIVED
63
+ | PlayerEvents.SCRIPTDATA_ARRIVED
64
+ | PlayerEvents.TIMED_ID3_METADATA_ARRIVED
65
+ | PlayerEvents.PGS_SUBTITLE_ARRIVED
66
+ | PlayerEvents.SYNCHRONOUS_KLV_METADATA_ARRIVED
67
+ | PlayerEvents.ASYNCHRONOUS_KLV_METADATA_ARRIVED
68
+ | PlayerEvents.SMPTE2038_METADATA_ARRIVED
69
+ | PlayerEvents.SEI_ARRIVED
70
+ | PlayerEvents.SCTE35_METADATA_ARRIVED
71
+ | PlayerEvents.PES_PRIVATE_DATA_DESCRIPTOR
72
+ | PlayerEvents.PES_PRIVATE_DATA_ARRIVED,
73
+ extraData: any,
74
+ };
75
+
76
+ export type WorkerMessagePacketTransmuxingEvent = WorkerMessagePacket & {
77
+ msg: 'transmuxing_event',
78
+ event: TransmuxingEvents,
79
+ };
80
+
81
+ export type WorkerMessagePacketTransmuxingEventInfo = WorkerMessagePacketTransmuxingEvent & {
82
+ msg: 'transmuxing_event',
83
+ event: TransmuxingEvents.MEDIA_INFO | TransmuxingEvents.STATISTICS_INFO,
84
+ info: any,
85
+ };
86
+
87
+ export type WorkerMessagePacketTransmuxingEventRecommendSeekpoint = WorkerMessagePacketTransmuxingEvent & {
88
+ msg: 'transmuxing_event',
89
+ event: TransmuxingEvents.RECOMMEND_SEEKPOINT,
90
+ milliseconds: number,
91
+ };
92
+
93
+ export type WorkerMessagePacketBufferedPositionChanged = WorkerMessagePacket & {
94
+ msg: 'buffered_position_changed',
95
+ buffered_position_milliseconds: number,
96
+ };
97
+
98
+ export type WorkerMessagePacketLogcatCallback = WorkerMessagePacket & {
99
+ msg: 'logcat_callback',
100
+ type: string,
101
+ logcat: string,
102
+ };