hls.js 1.5.13 → 1.5.14-0.canary.10415
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/README.md +4 -3
- package/dist/hls-demo.js +41 -38
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +4211 -2666
- package/dist/hls.js.d.ts +179 -110
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2841 -1921
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +2569 -1639
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +3572 -2017
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +38 -38
- package/src/config.ts +5 -2
- package/src/controller/abr-controller.ts +39 -25
- package/src/controller/audio-stream-controller.ts +156 -136
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +27 -10
- package/src/controller/base-stream-controller.ts +234 -89
- package/src/controller/buffer-controller.ts +250 -97
- package/src/controller/buffer-operation-queue.ts +16 -19
- package/src/controller/cap-level-controller.ts +3 -2
- package/src/controller/cmcd-controller.ts +51 -14
- package/src/controller/content-steering-controller.ts +29 -15
- package/src/controller/eme-controller.ts +10 -23
- package/src/controller/error-controller.ts +28 -22
- package/src/controller/fps-controller.ts +8 -3
- package/src/controller/fragment-finders.ts +44 -16
- package/src/controller/fragment-tracker.ts +58 -25
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/id3-track-controller.ts +45 -35
- package/src/controller/latency-controller.ts +18 -13
- package/src/controller/level-controller.ts +37 -19
- package/src/controller/stream-controller.ts +100 -83
- package/src/controller/subtitle-stream-controller.ts +35 -47
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +20 -22
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -16
- package/src/crypt/fast-aes-key.ts +28 -5
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +4 -3
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/audio/base-audio-demuxer.ts +16 -14
- package/src/demux/audio/mp3demuxer.ts +4 -3
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +8 -16
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +75 -38
- package/src/demux/video/avc-video-parser.ts +210 -121
- package/src/demux/video/base-video-parser.ts +135 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +749 -0
- package/src/events.ts +8 -1
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +84 -47
- package/src/loader/date-range.ts +71 -5
- package/src/loader/fragment-loader.ts +23 -21
- package/src/loader/fragment.ts +8 -4
- package/src/loader/key-loader.ts +3 -1
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +10 -9
- package/src/loader/m3u8-parser.ts +138 -144
- package/src/loader/playlist-loader.ts +5 -7
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +32 -62
- package/src/remux/passthrough-remuxer.ts +1 -1
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +3 -1
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +19 -6
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/media-playlist.ts +9 -1
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +96 -9
- package/src/utils/buffer-helper.ts +12 -31
- package/src/utils/cea-608-parser.ts +1 -3
- package/src/utils/codecs.ts +34 -5
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hash.ts +10 -0
- package/src/utils/hdr.ts +4 -7
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/level-helper.ts +71 -44
- package/src/utils/logger.ts +58 -23
- package/src/utils/mp4-tools.ts +5 -3
- package/src/utils/rendition-helper.ts +100 -74
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/variable-substitution.ts +0 -19
- package/src/utils/webvtt-parser.ts +2 -12
- package/src/demux/id3.ts +0 -411
- package/src/types/general.ts +0 -6
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import type {
|
2
2
|
ManifestLoadedData,
|
3
3
|
ManifestParsedData,
|
4
4
|
LevelLoadedData,
|
@@ -27,8 +27,6 @@ import type Hls from '../hls';
|
|
27
27
|
import type { HlsUrlParameters, LevelParsed } from '../types/level';
|
28
28
|
import type { MediaPlaylist } from '../types/media-playlist';
|
29
29
|
|
30
|
-
let chromeOrFirefox: boolean;
|
31
|
-
|
32
30
|
export default class LevelController extends BasePlaylistController {
|
33
31
|
private _levels: Level[] = [];
|
34
32
|
private _firstLevel: number = -1;
|
@@ -45,7 +43,7 @@ export default class LevelController extends BasePlaylistController {
|
|
45
43
|
hls: Hls,
|
46
44
|
contentSteeringController: ContentSteeringController | null,
|
47
45
|
) {
|
48
|
-
super(hls, '
|
46
|
+
super(hls, 'level-controller');
|
49
47
|
this.steering = contentSteeringController;
|
50
48
|
this._registerListeners();
|
51
49
|
}
|
@@ -119,22 +117,12 @@ export default class LevelController extends BasePlaylistController {
|
|
119
117
|
|
120
118
|
data.levels.forEach((levelParsed: LevelParsed) => {
|
121
119
|
const attributes = levelParsed.attrs;
|
122
|
-
|
123
|
-
// erase audio codec info if browser does not support mp4a.40.34.
|
124
|
-
// demuxer will autodetect codec and fallback to mpeg/audio
|
125
120
|
let { audioCodec, videoCodec } = levelParsed;
|
126
|
-
if (audioCodec?.indexOf('mp4a.40.34') !== -1) {
|
127
|
-
chromeOrFirefox ||= /chrome|firefox/i.test(navigator.userAgent);
|
128
|
-
if (chromeOrFirefox) {
|
129
|
-
levelParsed.audioCodec = audioCodec = undefined;
|
130
|
-
}
|
131
|
-
}
|
132
|
-
|
133
121
|
if (audioCodec) {
|
134
|
-
|
135
|
-
|
136
|
-
preferManagedMediaSource
|
137
|
-
|
122
|
+
// Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
|
123
|
+
levelParsed.audioCodec = audioCodec =
|
124
|
+
getCodecCompatibleName(audioCodec, preferManagedMediaSource) ||
|
125
|
+
undefined;
|
138
126
|
}
|
139
127
|
|
140
128
|
if (videoCodec?.indexOf('avc1') === 0) {
|
@@ -521,6 +509,30 @@ export default class LevelController extends BasePlaylistController {
|
|
521
509
|
this._startLevel = newLevel;
|
522
510
|
}
|
523
511
|
|
512
|
+
get pathwayPriority(): string[] | null {
|
513
|
+
if (this.steering) {
|
514
|
+
return this.steering.pathwayPriority;
|
515
|
+
}
|
516
|
+
|
517
|
+
return null;
|
518
|
+
}
|
519
|
+
|
520
|
+
set pathwayPriority(pathwayPriority: string[]) {
|
521
|
+
if (this.steering) {
|
522
|
+
const pathwaysList = this.steering.pathways();
|
523
|
+
const filteredPathwayPriority = pathwayPriority.filter((pathwayId) => {
|
524
|
+
return pathwaysList.indexOf(pathwayId) !== -1;
|
525
|
+
});
|
526
|
+
if (pathwayPriority.length < 1) {
|
527
|
+
this.warn(
|
528
|
+
`pathwayPriority ${pathwayPriority} should contain at least one pathway from list: ${pathwaysList}`,
|
529
|
+
);
|
530
|
+
return;
|
531
|
+
}
|
532
|
+
this.steering.pathwayPriority = filteredPathwayPriority;
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
524
536
|
protected onError(event: Events.ERROR, data: ErrorData) {
|
525
537
|
if (data.fatal || !data.context) {
|
526
538
|
return;
|
@@ -572,7 +584,13 @@ export default class LevelController extends BasePlaylistController {
|
|
572
584
|
if (curLevel.fragmentError === 0) {
|
573
585
|
curLevel.loadError = 0;
|
574
586
|
}
|
575
|
-
|
587
|
+
// Ignore matching details populated by loading a Media Playlist directly
|
588
|
+
let previousDetails = curLevel.details;
|
589
|
+
if (previousDetails === data.details && previousDetails.advanced) {
|
590
|
+
previousDetails = undefined;
|
591
|
+
}
|
592
|
+
|
593
|
+
this.playlistLoaded(level, data, previousDetails);
|
576
594
|
} else if (data.deliveryDirectives?.skip) {
|
577
595
|
// received a delta playlist update that cannot be merged
|
578
596
|
details.deltaUpdateFailed = true;
|
@@ -2,9 +2,14 @@ import BaseStreamController, { State } from './base-stream-controller';
|
|
2
2
|
import { changeTypeSupported } from '../is-supported';
|
3
3
|
import { Events } from '../events';
|
4
4
|
import { BufferHelper, BufferInfo } from '../utils/buffer-helper';
|
5
|
+
import { findFragmentByPTS } from './fragment-finders';
|
5
6
|
import { FragmentState } from './fragment-tracker';
|
6
7
|
import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
7
|
-
import {
|
8
|
+
import {
|
9
|
+
ElementaryStreamTypes,
|
10
|
+
Fragment,
|
11
|
+
MediaFragment,
|
12
|
+
} from '../loader/fragment';
|
8
13
|
import TransmuxerInterface from '../demux/transmuxer-interface';
|
9
14
|
import { ChunkMetadata } from '../types/transmuxer';
|
10
15
|
import GapController, { MAX_START_GAP_JUMP } from './gap-controller';
|
@@ -12,15 +17,15 @@ import { ErrorDetails } from '../errors';
|
|
12
17
|
import type { NetworkComponentAPI } from '../types/component-api';
|
13
18
|
import type Hls from '../hls';
|
14
19
|
import type { Level } from '../types/level';
|
15
|
-
import type { LevelDetails } from '../loader/level-details';
|
16
20
|
import type { FragmentTracker } from './fragment-tracker';
|
17
21
|
import type KeyLoader from '../loader/key-loader';
|
18
22
|
import type { TransmuxerResult } from '../types/transmuxer';
|
19
|
-
import type { TrackSet } from '../types/track';
|
23
|
+
import type { Track, TrackSet } from '../types/track';
|
20
24
|
import type { SourceBufferName } from '../types/buffer';
|
21
25
|
import type {
|
22
26
|
AudioTrackSwitchedData,
|
23
27
|
AudioTrackSwitchingData,
|
28
|
+
BufferCodecsData,
|
24
29
|
BufferCreatedData,
|
25
30
|
BufferEOSData,
|
26
31
|
BufferFlushedData,
|
@@ -49,8 +54,6 @@ export default class StreamController
|
|
49
54
|
private altAudio: boolean = false;
|
50
55
|
private audioOnly: boolean = false;
|
51
56
|
private fragPlaying: Fragment | null = null;
|
52
|
-
private onvplaying: EventListener | null = null;
|
53
|
-
private onvseeked: EventListener | null = null;
|
54
57
|
private fragLastKbps: number = 0;
|
55
58
|
private couldBacktrack: boolean = false;
|
56
59
|
private backtrackFragment: Fragment | null = null;
|
@@ -66,17 +69,15 @@ export default class StreamController
|
|
66
69
|
hls,
|
67
70
|
fragmentTracker,
|
68
71
|
keyLoader,
|
69
|
-
'
|
72
|
+
'stream-controller',
|
70
73
|
PlaylistLevelType.MAIN,
|
71
74
|
);
|
72
|
-
this.
|
75
|
+
this.registerListeners();
|
73
76
|
}
|
74
77
|
|
75
|
-
|
78
|
+
protected registerListeners() {
|
79
|
+
super.registerListeners();
|
76
80
|
const { hls } = this;
|
77
|
-
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
78
|
-
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
79
|
-
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
80
81
|
hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
81
82
|
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
82
83
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
@@ -85,7 +86,6 @@ export default class StreamController
|
|
85
86
|
this.onFragLoadEmergencyAborted,
|
86
87
|
this,
|
87
88
|
);
|
88
|
-
hls.on(Events.ERROR, this.onError, this);
|
89
89
|
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
90
90
|
hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
91
91
|
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -94,11 +94,9 @@ export default class StreamController
|
|
94
94
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
95
95
|
}
|
96
96
|
|
97
|
-
protected
|
97
|
+
protected unregisterListeners() {
|
98
|
+
super.unregisterListeners();
|
98
99
|
const { hls } = this;
|
99
|
-
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
100
|
-
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
101
|
-
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
102
100
|
hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
103
101
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
104
102
|
hls.off(
|
@@ -106,7 +104,6 @@ export default class StreamController
|
|
106
104
|
this.onFragLoadEmergencyAborted,
|
107
105
|
this,
|
108
106
|
);
|
109
|
-
hls.off(Events.ERROR, this.onError, this);
|
110
107
|
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
111
108
|
hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
|
112
109
|
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
@@ -116,7 +113,9 @@ export default class StreamController
|
|
116
113
|
}
|
117
114
|
|
118
115
|
protected onHandlerDestroying() {
|
119
|
-
|
116
|
+
// @ts-ignore
|
117
|
+
this.onMediaPlaying = this.onMediaSeeked = null;
|
118
|
+
this.unregisterListeners();
|
120
119
|
super.onHandlerDestroying();
|
121
120
|
}
|
122
121
|
|
@@ -220,6 +219,9 @@ export default class StreamController
|
|
220
219
|
}
|
221
220
|
|
222
221
|
private doTickIdle() {
|
222
|
+
if (!this.buffering) {
|
223
|
+
return;
|
224
|
+
}
|
223
225
|
const { hls, levelLastLoaded, levels, media } = this;
|
224
226
|
|
225
227
|
// if start level not parsed yet OR
|
@@ -363,7 +365,6 @@ export default class StreamController
|
|
363
365
|
) {
|
364
366
|
// Check if fragment is not loaded
|
365
367
|
const fragState = this.fragmentTracker.getState(frag);
|
366
|
-
this.fragCurrent = frag;
|
367
368
|
if (
|
368
369
|
fragState === FragmentState.NOT_LOADED ||
|
369
370
|
fragState === FragmentState.PARTIAL
|
@@ -376,7 +377,6 @@ export default class StreamController
|
|
376
377
|
);
|
377
378
|
this._loadBitrateTestFrag(frag, level);
|
378
379
|
} else {
|
379
|
-
this.startFragRequested = true;
|
380
380
|
super.loadFragment(frag, level, targetBufferTime);
|
381
381
|
}
|
382
382
|
} else {
|
@@ -516,10 +516,8 @@ export default class StreamController
|
|
516
516
|
) {
|
517
517
|
super.onMediaAttached(event, data);
|
518
518
|
const media = data.media;
|
519
|
-
|
520
|
-
|
521
|
-
media.addEventListener('playing', this.onvplaying as EventListener);
|
522
|
-
media.addEventListener('seeked', this.onvseeked as EventListener);
|
519
|
+
media.addEventListener('playing', this.onMediaPlaying);
|
520
|
+
media.addEventListener('seeked', this.onMediaSeeked);
|
523
521
|
this.gapController = new GapController(
|
524
522
|
this.config,
|
525
523
|
media,
|
@@ -530,12 +528,11 @@ export default class StreamController
|
|
530
528
|
|
531
529
|
protected onMediaDetaching() {
|
532
530
|
const { media } = this;
|
533
|
-
if (media
|
534
|
-
media.removeEventListener('playing', this.
|
535
|
-
media.removeEventListener('seeked', this.
|
536
|
-
this.onvplaying = this.onvseeked = null;
|
537
|
-
this.videoBuffer = null;
|
531
|
+
if (media) {
|
532
|
+
media.removeEventListener('playing', this.onMediaPlaying);
|
533
|
+
media.removeEventListener('seeked', this.onMediaSeeked);
|
538
534
|
}
|
535
|
+
this.videoBuffer = null;
|
539
536
|
this.fragPlaying = null;
|
540
537
|
if (this.gapController) {
|
541
538
|
this.gapController.destroy();
|
@@ -544,12 +541,12 @@ export default class StreamController
|
|
544
541
|
super.onMediaDetaching();
|
545
542
|
}
|
546
543
|
|
547
|
-
private onMediaPlaying() {
|
544
|
+
private onMediaPlaying = () => {
|
548
545
|
// tick to speed up FRAG_CHANGED triggering
|
549
546
|
this.tick();
|
550
|
-
}
|
547
|
+
};
|
551
548
|
|
552
|
-
private onMediaSeeked() {
|
549
|
+
private onMediaSeeked = () => {
|
553
550
|
const media = this.media;
|
554
551
|
const currentTime = media ? media.currentTime : null;
|
555
552
|
if (Number.isFinite(currentTime)) {
|
@@ -569,21 +566,17 @@ export default class StreamController
|
|
569
566
|
|
570
567
|
// tick to speed up FRAG_CHANGED triggering
|
571
568
|
this.tick();
|
572
|
-
}
|
569
|
+
};
|
573
570
|
|
574
|
-
|
571
|
+
protected onManifestLoading() {
|
572
|
+
super.onManifestLoading();
|
575
573
|
// reset buffer on manifest loading
|
576
574
|
this.log('Trigger BUFFER_RESET');
|
577
575
|
this.hls.trigger(Events.BUFFER_RESET, undefined);
|
578
|
-
this.fragmentTracker.removeAllFragments();
|
579
576
|
this.couldBacktrack = false;
|
580
|
-
this.
|
581
|
-
this.
|
582
|
-
|
583
|
-
this.backtrackFragment =
|
584
|
-
this.levelLastLoaded =
|
585
|
-
null;
|
586
|
-
this.altAudio = this.audioOnly = this.startFragRequested = false;
|
577
|
+
this.fragLastKbps = 0;
|
578
|
+
this.fragPlaying = this.backtrackFragment = null;
|
579
|
+
this.altAudio = this.audioOnly = false;
|
587
580
|
}
|
588
581
|
|
589
582
|
private onManifestParsed(
|
@@ -697,7 +690,8 @@ export default class StreamController
|
|
697
690
|
}
|
698
691
|
|
699
692
|
protected _handleFragmentLoadProgress(data: FragLoadedData) {
|
700
|
-
const
|
693
|
+
const frag = data.frag as MediaFragment;
|
694
|
+
const { part, payload } = data;
|
701
695
|
const { levels } = this;
|
702
696
|
if (!levels) {
|
703
697
|
this.warn(
|
@@ -706,7 +700,7 @@ export default class StreamController
|
|
706
700
|
return;
|
707
701
|
}
|
708
702
|
const currentLevel = levels[frag.level];
|
709
|
-
const details = currentLevel.details
|
703
|
+
const details = currentLevel.details;
|
710
704
|
if (!details) {
|
711
705
|
this.warn(
|
712
706
|
`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`,
|
@@ -735,7 +729,7 @@ export default class StreamController
|
|
735
729
|
const partial = partIndex !== -1;
|
736
730
|
const chunkMeta = new ChunkMetadata(
|
737
731
|
frag.level,
|
738
|
-
frag.sn
|
732
|
+
frag.sn,
|
739
733
|
frag.stats.chunkCount,
|
740
734
|
payload.byteLength,
|
741
735
|
partIndex,
|
@@ -879,12 +873,12 @@ export default class StreamController
|
|
879
873
|
(8 * stats.total) / (stats.buffering.end - stats.loading.first),
|
880
874
|
);
|
881
875
|
if (frag.sn !== 'initSegment') {
|
882
|
-
this.fragPrevious = frag;
|
876
|
+
this.fragPrevious = frag as MediaFragment;
|
883
877
|
}
|
884
878
|
this.fragBufferedComplete(frag, part);
|
885
879
|
}
|
886
880
|
|
887
|
-
|
881
|
+
protected onError(event: Events.ERROR, data: ErrorData) {
|
888
882
|
if (data.fatal) {
|
889
883
|
this.state = State.ERROR;
|
890
884
|
return;
|
@@ -942,8 +936,10 @@ export default class StreamController
|
|
942
936
|
|
943
937
|
if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
|
944
938
|
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
945
|
-
const
|
946
|
-
|
939
|
+
const state = this.state;
|
940
|
+
const activeFrag = state !== State.IDLE ? this.fragCurrent : null;
|
941
|
+
const levelDetails = this.getLevelDetails();
|
942
|
+
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
947
943
|
}
|
948
944
|
|
949
945
|
this.lastCurrentTime = media.currentTime;
|
@@ -955,7 +951,7 @@ export default class StreamController
|
|
955
951
|
// in that case, reset startFragRequested flag
|
956
952
|
if (!this.loadedmetadata) {
|
957
953
|
this.startFragRequested = false;
|
958
|
-
this.nextLoadPosition = this.
|
954
|
+
this.nextLoadPosition = this.lastCurrentTime;
|
959
955
|
}
|
960
956
|
this.tickImmediate();
|
961
957
|
}
|
@@ -1067,7 +1063,7 @@ export default class StreamController
|
|
1067
1063
|
}
|
1068
1064
|
|
1069
1065
|
private _handleTransmuxComplete(transmuxResult: TransmuxerResult) {
|
1070
|
-
const id =
|
1066
|
+
const id = this.playlistType;
|
1071
1067
|
const { hls } = this;
|
1072
1068
|
const { remuxResult, chunkMeta } = transmuxResult;
|
1073
1069
|
|
@@ -1117,7 +1113,7 @@ export default class StreamController
|
|
1117
1113
|
}
|
1118
1114
|
|
1119
1115
|
// Avoid buffering if backtracking this fragment
|
1120
|
-
if (video && details
|
1116
|
+
if (video && details) {
|
1121
1117
|
const prevFrag = details.fragments[frag.sn - 1 - details.startSN];
|
1122
1118
|
const isFirstFragment = frag.sn === details.startSN;
|
1123
1119
|
const isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc;
|
@@ -1307,6 +1303,7 @@ export default class StreamController
|
|
1307
1303
|
currentLevel.audioCodec || ''
|
1308
1304
|
}/${audio.codec}]`,
|
1309
1305
|
);
|
1306
|
+
delete tracks.audiovideo;
|
1310
1307
|
}
|
1311
1308
|
if (video) {
|
1312
1309
|
video.levelCodec = currentLevel.videoCodec;
|
@@ -1318,28 +1315,34 @@ export default class StreamController
|
|
1318
1315
|
video.codec
|
1319
1316
|
}]`,
|
1320
1317
|
);
|
1318
|
+
delete tracks.audiovideo;
|
1321
1319
|
}
|
1322
1320
|
if (audiovideo) {
|
1323
1321
|
this.log(
|
1324
1322
|
`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`,
|
1325
1323
|
);
|
1324
|
+
delete tracks.video;
|
1325
|
+
delete tracks.audio;
|
1326
|
+
}
|
1327
|
+
const trackTypes = Object.keys(tracks);
|
1328
|
+
if (trackTypes.length) {
|
1329
|
+
this.hls.trigger(Events.BUFFER_CODECS, tracks as BufferCodecsData);
|
1330
|
+
// loop through tracks that are going to be provided to bufferController
|
1331
|
+
trackTypes.forEach((trackName) => {
|
1332
|
+
const track = tracks[trackName] as Track;
|
1333
|
+
const initSegment = track.initSegment;
|
1334
|
+
if (initSegment?.byteLength) {
|
1335
|
+
this.hls.trigger(Events.BUFFER_APPENDING, {
|
1336
|
+
type: trackName as SourceBufferName,
|
1337
|
+
data: initSegment,
|
1338
|
+
frag,
|
1339
|
+
part: null,
|
1340
|
+
chunkMeta,
|
1341
|
+
parent: frag.type,
|
1342
|
+
});
|
1343
|
+
}
|
1344
|
+
});
|
1326
1345
|
}
|
1327
|
-
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
1328
|
-
// loop through tracks that are going to be provided to bufferController
|
1329
|
-
Object.keys(tracks).forEach((trackName) => {
|
1330
|
-
const track = tracks[trackName];
|
1331
|
-
const initSegment = track.initSegment;
|
1332
|
-
if (initSegment?.byteLength) {
|
1333
|
-
this.hls.trigger(Events.BUFFER_APPENDING, {
|
1334
|
-
type: trackName as SourceBufferName,
|
1335
|
-
data: initSegment,
|
1336
|
-
frag,
|
1337
|
-
part: null,
|
1338
|
-
chunkMeta,
|
1339
|
-
parent: frag.type,
|
1340
|
-
});
|
1341
|
-
}
|
1342
|
-
});
|
1343
1346
|
// trigger handler right now
|
1344
1347
|
this.tickImmediate();
|
1345
1348
|
}
|
@@ -1351,6 +1354,15 @@ export default class StreamController
|
|
1351
1354
|
);
|
1352
1355
|
}
|
1353
1356
|
|
1357
|
+
public get maxBufferLength(): number {
|
1358
|
+
const { levels, level } = this;
|
1359
|
+
const levelInfo = levels?.[level];
|
1360
|
+
if (!levelInfo) {
|
1361
|
+
return this.config.maxBufferLength;
|
1362
|
+
}
|
1363
|
+
return this.getMaxBufferLength(levelInfo.maxBitrate);
|
1364
|
+
}
|
1365
|
+
|
1354
1366
|
private backtrack(frag: Fragment) {
|
1355
1367
|
this.couldBacktrack = true;
|
1356
1368
|
// Causes findFragments to backtrack through fragments to find the keyframe
|
@@ -1415,26 +1427,31 @@ export default class StreamController
|
|
1415
1427
|
}
|
1416
1428
|
|
1417
1429
|
get currentFrag(): Fragment | null {
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1430
|
+
if (this.fragPlaying) {
|
1431
|
+
return this.fragPlaying;
|
1432
|
+
}
|
1433
|
+
const currentTime = this.media?.currentTime || this.lastCurrentTime;
|
1434
|
+
if (Number.isFinite(currentTime)) {
|
1435
|
+
return this.getAppendedFrag(currentTime);
|
1421
1436
|
}
|
1422
1437
|
return null;
|
1423
1438
|
}
|
1424
1439
|
|
1425
1440
|
get currentProgramDateTime(): Date | null {
|
1426
|
-
const
|
1427
|
-
if (
|
1428
|
-
const
|
1429
|
-
const frag =
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
) {
|
1435
|
-
const
|
1436
|
-
|
1437
|
-
|
1441
|
+
const currentTime = this.media?.currentTime || this.lastCurrentTime;
|
1442
|
+
if (Number.isFinite(currentTime)) {
|
1443
|
+
const details = this.getLevelDetails();
|
1444
|
+
const frag =
|
1445
|
+
this.currentFrag ||
|
1446
|
+
(details
|
1447
|
+
? findFragmentByPTS(null, details.fragments, currentTime)
|
1448
|
+
: null);
|
1449
|
+
if (frag) {
|
1450
|
+
const programDateTime = frag.programDateTime;
|
1451
|
+
if (programDateTime !== null) {
|
1452
|
+
const epocMs = programDateTime + (currentTime - frag.start) * 1000;
|
1453
|
+
return new Date(epocMs);
|
1454
|
+
}
|
1438
1455
|
}
|
1439
1456
|
}
|
1440
1457
|
return null;
|