@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
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alessmicrosystems/mpegts.js",
|
|
3
|
+
"version": "1.8.1",
|
|
4
|
+
"description": "HTML5 MPEG2-TS Stream Player",
|
|
5
|
+
"main": "./dist/mpegts.js",
|
|
6
|
+
"types": "./d.ts/mpegts.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/alessblaze/mpegts.js.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"html5",
|
|
13
|
+
"mpegts",
|
|
14
|
+
"m2ts",
|
|
15
|
+
"mse",
|
|
16
|
+
"arib",
|
|
17
|
+
"dvb"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "webpack --mode=production --progress",
|
|
21
|
+
"build:debug": "webpack --mode=development --progress",
|
|
22
|
+
"watch": "webpack --mode=development --progress --watch",
|
|
23
|
+
"serve": "webpack serve --mode=development --progress"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"es6-promise": "^4.2.5"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/express-serve-static-core": "4.17.37",
|
|
30
|
+
"@types/node": "^10.12.18",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
|
32
|
+
"@typescript-eslint/parser": "^8.59.3",
|
|
33
|
+
"browser-sync": "^3.0.4",
|
|
34
|
+
"eslint": "^10.4.0",
|
|
35
|
+
"exports-loader": "^0.7.0",
|
|
36
|
+
"file-loader": "^3.0.1",
|
|
37
|
+
"loader-utils": ">=1.4.1",
|
|
38
|
+
"minimist": ">=1.2.6",
|
|
39
|
+
"socket.io-parser": ">=3.3.3",
|
|
40
|
+
"source-map-loader": "^0.2.4",
|
|
41
|
+
"ssri": ">=8.0.1",
|
|
42
|
+
"terser-webpack-plugin": "^5.6.0",
|
|
43
|
+
"ts-loader": "^8.4.0",
|
|
44
|
+
"typescript": "^5.2.2",
|
|
45
|
+
"webpack": "^5.106.2",
|
|
46
|
+
"webpack-cli": "^5.1.4",
|
|
47
|
+
"webpack-dev-server": "^5.2.4",
|
|
48
|
+
"ws": ">=7.4.6",
|
|
49
|
+
"xmlhttprequest-ssl": ">=1.6.2"
|
|
50
|
+
},
|
|
51
|
+
"author": "Albert Blasczykowski <albertalesso@gmail.com>",
|
|
52
|
+
"license": "Apache-2.0"
|
|
53
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
export const defaultConfig = {
|
|
20
|
+
enableWorker: false,
|
|
21
|
+
enableWorkerForMSE: false,
|
|
22
|
+
enableStashBuffer: true,
|
|
23
|
+
stashInitialSize: undefined,
|
|
24
|
+
|
|
25
|
+
isLive: false,
|
|
26
|
+
|
|
27
|
+
preferredAudioPid: 0,
|
|
28
|
+
|
|
29
|
+
liveBufferLatencyChasing: false,
|
|
30
|
+
liveBufferLatencyChasingOnPaused: false,
|
|
31
|
+
liveBufferLatencyMaxLatency: 1.5,
|
|
32
|
+
liveBufferLatencyMinRemain: 0.5,
|
|
33
|
+
|
|
34
|
+
liveSync: false,
|
|
35
|
+
liveSyncMaxLatency: 1.2,
|
|
36
|
+
liveSyncTargetLatency: 0.8,
|
|
37
|
+
liveSyncPlaybackRate: 1.2,
|
|
38
|
+
|
|
39
|
+
lazyLoad: true,
|
|
40
|
+
lazyLoadMaxDuration: 3 * 60,
|
|
41
|
+
lazyLoadRecoverDuration: 30,
|
|
42
|
+
deferLoadAfterSourceOpen: true,
|
|
43
|
+
|
|
44
|
+
// autoCleanupSourceBuffer: default as false, leave unspecified
|
|
45
|
+
autoCleanupMaxBackwardDuration: 3 * 60,
|
|
46
|
+
autoCleanupMinBackwardDuration: 2 * 60,
|
|
47
|
+
|
|
48
|
+
statisticsInfoReportInterval: 600,
|
|
49
|
+
|
|
50
|
+
fixAudioTimestampGap: true,
|
|
51
|
+
|
|
52
|
+
accurateSeek: false,
|
|
53
|
+
seekType: 'range', // [range, param, custom]
|
|
54
|
+
seekParamStart: 'bstart',
|
|
55
|
+
seekParamEnd: 'bend',
|
|
56
|
+
rangeLoadZeroStart: false,
|
|
57
|
+
customSeekHandler: undefined,
|
|
58
|
+
reuseRedirectedURL: false,
|
|
59
|
+
// referrerPolicy: leave as unspecified
|
|
60
|
+
|
|
61
|
+
headers: undefined,
|
|
62
|
+
customLoader: undefined
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function createDefaultConfig() {
|
|
66
|
+
return Object.assign({}, defaultConfig);
|
|
67
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
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 IOController from '../io/io-controller.js';
|
|
20
|
+
import {createDefaultConfig} from '../config.js';
|
|
21
|
+
|
|
22
|
+
class Features {
|
|
23
|
+
|
|
24
|
+
static supportMSEH264Playback() {
|
|
25
|
+
const avc_aac_mime_type = 'video/mp4; codecs="avc1.42E01E,mp4a.40.2"';
|
|
26
|
+
const support_w3c_mse = self.MediaSource && self.MediaSource.isTypeSupported(avc_aac_mime_type);
|
|
27
|
+
const support_apple_mme = self.ManagedMediaSource && self.ManagedMediaSource.isTypeSupported(avc_aac_mime_type);
|
|
28
|
+
return support_w3c_mse || support_apple_mme;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static supportMSEH265Playback() {
|
|
32
|
+
const hevc_mime_type = 'video/mp4; codecs="hvc1.1.6.L93.B0"';
|
|
33
|
+
const support_w3c_mse = self.MediaSource && self.MediaSource.isTypeSupported(hevc_mime_type);
|
|
34
|
+
const support_apple_mme = self.ManagedMediaSource && self.ManagedMediaSource.isTypeSupported(hevc_mime_type);
|
|
35
|
+
return support_w3c_mse || support_apple_mme;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static supportNetworkStreamIO() {
|
|
39
|
+
let ioctl = new IOController({}, createDefaultConfig());
|
|
40
|
+
let loaderType = ioctl.loaderType;
|
|
41
|
+
ioctl.destroy();
|
|
42
|
+
return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static getNetworkLoaderTypeName() {
|
|
46
|
+
let ioctl = new IOController({}, createDefaultConfig());
|
|
47
|
+
let loaderType = ioctl.loaderType;
|
|
48
|
+
ioctl.destroy();
|
|
49
|
+
return loaderType;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static supportNativeMediaPlayback(mimeType) {
|
|
53
|
+
if (Features.videoElement == undefined) {
|
|
54
|
+
Features.videoElement = window.document.createElement('video');
|
|
55
|
+
}
|
|
56
|
+
let canPlay = Features.videoElement.canPlayType(mimeType);
|
|
57
|
+
return canPlay === 'probably' || canPlay == 'maybe';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static getFeatureList() {
|
|
61
|
+
let features = {
|
|
62
|
+
msePlayback: false,
|
|
63
|
+
mseLivePlayback: false,
|
|
64
|
+
mseH265Playback: false,
|
|
65
|
+
networkStreamIO: false,
|
|
66
|
+
networkLoaderName: '',
|
|
67
|
+
nativeMP4H264Playback: false,
|
|
68
|
+
nativeMP4H265Playback: false,
|
|
69
|
+
nativeWebmVP8Playback: false,
|
|
70
|
+
nativeWebmVP9Playback: false
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
features.msePlayback = Features.supportMSEH264Playback();
|
|
74
|
+
features.networkStreamIO = Features.supportNetworkStreamIO();
|
|
75
|
+
features.networkLoaderName = Features.getNetworkLoaderTypeName();
|
|
76
|
+
features.mseLivePlayback = features.msePlayback && features.networkStreamIO;
|
|
77
|
+
features.mseH265Playback = Features.supportMSEH265Playback();
|
|
78
|
+
features.nativeMP4H264Playback = Features.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"');
|
|
79
|
+
features.nativeMP4H265Playback = Features.supportNativeMediaPlayback('video/mp4; codecs="hvc1.1.6.L93.B0"');
|
|
80
|
+
features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"');
|
|
81
|
+
features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp9"');
|
|
82
|
+
|
|
83
|
+
return features;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default Features;
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
class MediaInfo {
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
this.mimeType = null;
|
|
23
|
+
this.duration = null;
|
|
24
|
+
|
|
25
|
+
this.hasAudio = null;
|
|
26
|
+
this.hasVideo = null;
|
|
27
|
+
this.audioCodec = null;
|
|
28
|
+
this.videoCodec = null;
|
|
29
|
+
this.audioDataRate = null;
|
|
30
|
+
this.videoDataRate = null;
|
|
31
|
+
|
|
32
|
+
this.audioSampleRate = null;
|
|
33
|
+
this.audioChannelCount = null;
|
|
34
|
+
|
|
35
|
+
this.width = null;
|
|
36
|
+
this.height = null;
|
|
37
|
+
this.fps = null;
|
|
38
|
+
this.profile = null;
|
|
39
|
+
this.level = null;
|
|
40
|
+
this.refFrames = null;
|
|
41
|
+
this.chromaFormat = null;
|
|
42
|
+
this.sarNum = null;
|
|
43
|
+
this.sarDen = null;
|
|
44
|
+
|
|
45
|
+
this.metadata = null;
|
|
46
|
+
this.segments = null; // MediaInfo[]
|
|
47
|
+
this.segmentCount = null;
|
|
48
|
+
this.hasKeyframesIndex = null;
|
|
49
|
+
this.keyframesIndex = null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
isComplete() {
|
|
53
|
+
let audioInfoComplete = (this.hasAudio === false) ||
|
|
54
|
+
(this.hasAudio === true &&
|
|
55
|
+
this.audioCodec != null &&
|
|
56
|
+
this.audioSampleRate != null &&
|
|
57
|
+
this.audioChannelCount != null);
|
|
58
|
+
|
|
59
|
+
let videoInfoComplete = (this.hasVideo === false) ||
|
|
60
|
+
(this.hasVideo === true &&
|
|
61
|
+
this.videoCodec != null &&
|
|
62
|
+
this.width != null &&
|
|
63
|
+
this.height != null &&
|
|
64
|
+
this.fps != null &&
|
|
65
|
+
this.profile != null &&
|
|
66
|
+
this.level != null &&
|
|
67
|
+
this.refFrames != null &&
|
|
68
|
+
this.chromaFormat != null &&
|
|
69
|
+
this.sarNum != null &&
|
|
70
|
+
this.sarDen != null);
|
|
71
|
+
|
|
72
|
+
// keyframesIndex may not be present
|
|
73
|
+
return this.mimeType != null &&
|
|
74
|
+
audioInfoComplete &&
|
|
75
|
+
videoInfoComplete;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isSeekable() {
|
|
79
|
+
return this.hasKeyframesIndex === true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getNearestKeyframe(milliseconds) {
|
|
83
|
+
if (this.keyframesIndex == null) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let table = this.keyframesIndex;
|
|
88
|
+
let keyframeIdx = this._search(table.times, milliseconds);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
index: keyframeIdx,
|
|
92
|
+
milliseconds: table.times[keyframeIdx],
|
|
93
|
+
fileposition: table.filepositions[keyframeIdx]
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_search(list, value) {
|
|
98
|
+
let idx = 0;
|
|
99
|
+
|
|
100
|
+
let last = list.length - 1;
|
|
101
|
+
let mid = 0;
|
|
102
|
+
let lbound = 0;
|
|
103
|
+
let ubound = last;
|
|
104
|
+
|
|
105
|
+
if (value < list[0]) {
|
|
106
|
+
idx = 0;
|
|
107
|
+
lbound = ubound + 1; // skip search
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
while (lbound <= ubound) {
|
|
111
|
+
mid = lbound + Math.floor((ubound - lbound) / 2);
|
|
112
|
+
if (mid === last || (value >= list[mid] && value < list[mid + 1])) {
|
|
113
|
+
idx = mid;
|
|
114
|
+
break;
|
|
115
|
+
} else if (list[mid] < value) {
|
|
116
|
+
lbound = mid + 1;
|
|
117
|
+
} else {
|
|
118
|
+
ubound = mid - 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return idx;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default MediaInfo;
|
|
@@ -0,0 +1,230 @@
|
|
|
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
|
+
// Represents an media sample (audio / video)
|
|
20
|
+
export class SampleInfo {
|
|
21
|
+
|
|
22
|
+
constructor(dts, pts, duration, originalDts, isSync) {
|
|
23
|
+
this.dts = dts;
|
|
24
|
+
this.pts = pts;
|
|
25
|
+
this.duration = duration;
|
|
26
|
+
this.originalDts = originalDts;
|
|
27
|
+
this.isSyncPoint = isSync;
|
|
28
|
+
this.fileposition = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Media Segment concept is defined in Media Source Extensions spec.
|
|
34
|
+
// Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box.
|
|
35
|
+
export class MediaSegmentInfo {
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this.beginDts = 0;
|
|
39
|
+
this.endDts = 0;
|
|
40
|
+
this.beginPts = 0;
|
|
41
|
+
this.endPts = 0;
|
|
42
|
+
this.originalBeginDts = 0;
|
|
43
|
+
this.originalEndDts = 0;
|
|
44
|
+
this.syncPoints = []; // SampleInfo[n], for video IDR frames only
|
|
45
|
+
this.firstSample = null; // SampleInfo
|
|
46
|
+
this.lastSample = null; // SampleInfo
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
appendSyncPoint(sampleInfo) { // also called Random Access Point
|
|
50
|
+
sampleInfo.isSyncPoint = true;
|
|
51
|
+
this.syncPoints.push(sampleInfo);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Ordered list for recording video IDR frames, sorted by originalDts
|
|
57
|
+
export class IDRSampleList {
|
|
58
|
+
|
|
59
|
+
constructor() {
|
|
60
|
+
this._list = [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
clear() {
|
|
64
|
+
this._list = [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
appendArray(syncPoints) {
|
|
68
|
+
let list = this._list;
|
|
69
|
+
|
|
70
|
+
if (syncPoints.length === 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) {
|
|
75
|
+
this.clear();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Array.prototype.push.apply(list, syncPoints);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getLastSyncPointBeforeDts(dts) {
|
|
82
|
+
if (this._list.length == 0) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let list = this._list;
|
|
87
|
+
let idx = 0;
|
|
88
|
+
let last = list.length - 1;
|
|
89
|
+
let mid = 0;
|
|
90
|
+
let lbound = 0;
|
|
91
|
+
let ubound = last;
|
|
92
|
+
|
|
93
|
+
if (dts < list[0].dts) {
|
|
94
|
+
idx = 0;
|
|
95
|
+
lbound = ubound + 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
while (lbound <= ubound) {
|
|
99
|
+
mid = lbound + Math.floor((ubound - lbound) / 2);
|
|
100
|
+
if (mid === last || (dts >= list[mid].dts && dts < list[mid + 1].dts)) {
|
|
101
|
+
idx = mid;
|
|
102
|
+
break;
|
|
103
|
+
} else if (list[mid].dts < dts) {
|
|
104
|
+
lbound = mid + 1;
|
|
105
|
+
} else {
|
|
106
|
+
ubound = mid - 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return this._list[idx];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Data structure for recording information of media segments in single track.
|
|
115
|
+
export class MediaSegmentInfoList {
|
|
116
|
+
|
|
117
|
+
constructor(type) {
|
|
118
|
+
this._type = type;
|
|
119
|
+
this._list = [];
|
|
120
|
+
this._lastAppendLocation = -1; // cached last insert location
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get type() {
|
|
124
|
+
return this._type;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get length() {
|
|
128
|
+
return this._list.length;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isEmpty() {
|
|
132
|
+
return this._list.length === 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
clear() {
|
|
136
|
+
this._list = [];
|
|
137
|
+
this._lastAppendLocation = -1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_searchNearestSegmentBefore(originalBeginDts) {
|
|
141
|
+
let list = this._list;
|
|
142
|
+
if (list.length === 0) {
|
|
143
|
+
return -2;
|
|
144
|
+
}
|
|
145
|
+
let last = list.length - 1;
|
|
146
|
+
let mid = 0;
|
|
147
|
+
let lbound = 0;
|
|
148
|
+
let ubound = last;
|
|
149
|
+
|
|
150
|
+
let idx = 0;
|
|
151
|
+
|
|
152
|
+
if (originalBeginDts < list[0].originalBeginDts) {
|
|
153
|
+
idx = -1;
|
|
154
|
+
return idx;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
while (lbound <= ubound) {
|
|
158
|
+
mid = lbound + Math.floor((ubound - lbound) / 2);
|
|
159
|
+
if (mid === last || (originalBeginDts > list[mid].lastSample.originalDts &&
|
|
160
|
+
(originalBeginDts < list[mid + 1].originalBeginDts))) {
|
|
161
|
+
idx = mid;
|
|
162
|
+
break;
|
|
163
|
+
} else if (list[mid].originalBeginDts < originalBeginDts) {
|
|
164
|
+
lbound = mid + 1;
|
|
165
|
+
} else {
|
|
166
|
+
ubound = mid - 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return idx;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_searchNearestSegmentAfter(originalBeginDts) {
|
|
173
|
+
return this._searchNearestSegmentBefore(originalBeginDts) + 1;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
append(mediaSegmentInfo) {
|
|
177
|
+
let list = this._list;
|
|
178
|
+
let msi = mediaSegmentInfo;
|
|
179
|
+
let lastAppendIdx = this._lastAppendLocation;
|
|
180
|
+
let insertIdx = 0;
|
|
181
|
+
|
|
182
|
+
if (lastAppendIdx !== -1 && lastAppendIdx < list.length &&
|
|
183
|
+
msi.originalBeginDts >= list[lastAppendIdx].lastSample.originalDts &&
|
|
184
|
+
((lastAppendIdx === list.length - 1) ||
|
|
185
|
+
(lastAppendIdx < list.length - 1 &&
|
|
186
|
+
msi.originalBeginDts < list[lastAppendIdx + 1].originalBeginDts))) {
|
|
187
|
+
insertIdx = lastAppendIdx + 1; // use cached location idx
|
|
188
|
+
} else {
|
|
189
|
+
if (list.length > 0) {
|
|
190
|
+
insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this._lastAppendLocation = insertIdx;
|
|
195
|
+
this._list.splice(insertIdx, 0, msi);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getLastSegmentBefore(originalBeginDts) {
|
|
199
|
+
let idx = this._searchNearestSegmentBefore(originalBeginDts);
|
|
200
|
+
if (idx >= 0) {
|
|
201
|
+
return this._list[idx];
|
|
202
|
+
} else { // -1
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getLastSampleBefore(originalBeginDts) {
|
|
208
|
+
let segment = this.getLastSegmentBefore(originalBeginDts);
|
|
209
|
+
if (segment != null) {
|
|
210
|
+
return segment.lastSample;
|
|
211
|
+
} else {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getLastSyncPointBefore(originalBeginDts) {
|
|
217
|
+
let segmentIdx = this._searchNearestSegmentBefore(originalBeginDts);
|
|
218
|
+
let syncPoints = this._list[segmentIdx].syncPoints;
|
|
219
|
+
while (syncPoints.length === 0 && segmentIdx > 0) {
|
|
220
|
+
segmentIdx--;
|
|
221
|
+
syncPoints = this._list[segmentIdx].syncPoints;
|
|
222
|
+
}
|
|
223
|
+
if (syncPoints.length > 0) {
|
|
224
|
+
return syncPoints[syncPoints.length - 1];
|
|
225
|
+
} else {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|