Skip to content

Commit 00951c4

Browse files
authored
Merge branch 'main' into fix/dash-media-sequence
2 parents 6967321 + de0b55b commit 00951c4

17 files changed

+1447
-1031
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
ci:
1919
needs: should-skip
20-
if: ${{needs.should-skip.outputs.should-skip-job != 'true'}}
20+
if: ${{needs.should-skip.outputs.should-skip-job != 'true' || github.ref == 'refs/heads/main'}}
2121
strategy:
2222
fail-fast: false
2323
matrix:

index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ <h3>Options</h3>
8080
<input id=partial type="checkbox">
8181
Handle Partial (reloads player)
8282
</label>
83+
<label>
84+
<input id=llhls type="checkbox">
85+
[EXPERIMENTAL] Enables support for ll-hls (reloads player)
86+
</label>
8387
<label>
8488
<input id=buffer-water type="checkbox">
8589
[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)

package-lock.json

Lines changed: 986 additions & 897 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
"@videojs/vhs-utils": "^3.0.0",
6161
"aes-decrypter": "3.1.2",
6262
"global": "^4.4.0",
63-
"m3u8-parser": "4.5.2",
64-
"mpd-parser": "0.15.4",
65-
"mux.js": "5.10.0",
63+
"m3u8-parser": "4.6.0",
64+
"mpd-parser": "0.16.0",
65+
"mux.js": "5.11.0",
6666
"video.js": "^6 || ^7"
6767
},
6868
"devDependencies": {

scripts/index-demo-page.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
'sync-workers',
242242
'liveui',
243243
'partial',
244+
'llhls',
244245
'url',
245246
'type',
246247
'keysystems',
@@ -300,6 +301,13 @@
300301
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
301302
});
302303

304+
stateEls.llhls.addEventListener('change', function(event) {
305+
saveState();
306+
307+
// reload the player and scripts
308+
stateEls.minified.dispatchEvent(newEvent('change'));
309+
});
310+
303311
stateEls.partial.addEventListener('change', function(event) {
304312
saveState();
305313

@@ -377,7 +385,8 @@
377385
vhs: {
378386
overrideNative: getInputValue(stateEls['override-native']),
379387
handlePartialData: getInputValue(stateEls.partial),
380-
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water'])
388+
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
389+
experimentalLLHLS: getInputValue(stateEls.llhls)
381390
}
382391
}
383392
});

scripts/rollup.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ let syncWorker;
1212
const options = {
1313
input: 'src/videojs-http-streaming.js',
1414
distName: 'videojs-http-streaming',
15-
checkWatch: false,
1615
excludeCoverage(defaults) {
1716
defaults.push(/^rollup-plugin-worker-factory/);
1817
defaults.push(/^create-test-data!/);

src/dash-playlist-loader.js

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import videojs from 'video.js';
22
import {
33
parse as parseMpd,
44
addSidxSegmentsToPlaylist,
5+
generateSidxKey,
56
parseUTCTiming
67
} from 'mpd-parser';
78
import {
@@ -126,18 +127,6 @@ export const parseMasterXml = ({ masterXml, srcUrl, clientOffset, sidxMapping })
126127
return master;
127128
};
128129

129-
export const generateSidxKey = (sidxInfo) => {
130-
// should be non-inclusive
131-
const sidxByteRangeEnd =
132-
sidxInfo.byterange.offset +
133-
sidxInfo.byterange.length -
134-
1;
135-
136-
return sidxInfo.uri + '-' +
137-
sidxInfo.byterange.offset + '-' +
138-
sidxByteRangeEnd;
139-
};
140-
141130
/**
142131
* Returns a new master manifest that is the result of merging an updated master manifest
143132
* into the original version.
@@ -364,20 +353,27 @@ export default class DashPlaylistLoader extends EventTarget {
364353

365354
// resolve the segment URL relative to the playlist
366355
const uri = resolveManifestRedirect(this.handleManifestRedirects, playlist.sidx.resolvedUri);
367-
const sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
368-
369-
sidxMapping[sidxKey] = {
370-
sidxInfo: playlist.sidx
371-
};
372356

373357
const fin = (err, request) => {
374358
if (this.requestErrored_(err, request, startingState)) {
375359
return;
376360
}
377361

378-
const sidx = parseSidx(toUint8(request.response).subarray(8));
362+
const sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
363+
let sidx;
379364

380-
sidxMapping[sidxKey].sidx = sidx;
365+
try {
366+
sidx = parseSidx(toUint8(request.response).subarray(8));
367+
} catch (e) {
368+
// sidx parsing failed.
369+
this.requestErrored_(e, request, startingState);
370+
return;
371+
}
372+
373+
sidxMapping[sidxKey] = {
374+
sidxInfo: playlist.sidx,
375+
sidx
376+
};
381377

382378
addSidxSegmentsToPlaylist(playlist, sidx, playlist.sidx.resolvedUri);
383379

src/manifest.js

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ export const createPlaylistID = (index, uri) => {
1212
/**
1313
* Parses a given m3u8 playlist
1414
*
15+
* @param {Function} [onwarn]
16+
* a function to call when the parser triggers a warning event.
17+
* @param {Function} [oninfo]
18+
* a function to call when the parser triggers an info event.
1519
* @param {string} manifestString
1620
* The downloaded manifest string
1721
* @param {Object[]} [customTagParsers]
1822
* An array of custom tag parsers for the m3u8-parser instance
1923
* @param {Object[]} [customTagMappers]
20-
* An array of custom tag mappers for the m3u8-parser instance
24+
* An array of custom tag mappers for the m3u8-parser instance
25+
* @param {boolean} [experimentalLLHLS=false]
26+
* Whether to keep ll-hls features in the manifest after parsing.
2127
* @return {Object}
2228
* The manifest object
2329
*/
@@ -26,7 +32,8 @@ export const parseManifest = ({
2632
oninfo,
2733
manifestString,
2834
customTagParsers = [],
29-
customTagMappers = []
35+
customTagMappers = [],
36+
experimentalLLHLS
3037
}) => {
3138
const parser = new M3u8Parser();
3239

@@ -43,7 +50,36 @@ export const parseManifest = ({
4350
parser.push(manifestString);
4451
parser.end();
4552

46-
return parser.manifest;
53+
const manifest = parser.manifest;
54+
55+
// remove llhls features from the parsed manifest
56+
// if we don't want llhls support.
57+
if (!experimentalLLHLS) {
58+
[
59+
'preloadSegment',
60+
'skip',
61+
'serverControl',
62+
'renditionReports',
63+
'partInf',
64+
'partTargetDuration'
65+
].forEach(function(k) {
66+
if (manifest.hasOwnProperty(k)) {
67+
delete manifest[k];
68+
}
69+
});
70+
71+
if (manifest.segments) {
72+
manifest.segments.forEach(function(segment) {
73+
['parts', 'preloadHints'].forEach(function(k) {
74+
if (segment.hasOwnProperty(k)) {
75+
delete segment[k];
76+
}
77+
});
78+
});
79+
}
80+
}
81+
82+
return manifest;
4783
};
4884

4985
/**

src/media-segment-request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ export const mediaSegmentRequest = ({
971971
}
972972

973973
const segmentRequestOptions = videojs.mergeOptions(xhrOptions, {
974-
uri: segment.resolvedUri,
974+
uri: segment.part && segment.part.resolvedUri || segment.resolvedUri,
975975
responseType: 'arraybuffer',
976976
headers: segmentXhrHeaders(segment)
977977
});

src/playlist-loader.js

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,73 @@ import {
1919
const { mergeOptions, EventTarget } = videojs;
2020

2121
/**
22-
* Returns a new array of segments that is the result of merging
23-
* properties from an older list of segments onto an updated
24-
* list. No properties on the updated playlist will be overridden.
25-
*
26-
* @param {Array} original the outdated list of segments
27-
* @param {Array} update the updated list of segments
28-
* @param {number=} offset the index of the first update
29-
* segment in the original segment list. For non-live playlists,
30-
* this should always be zero and does not need to be
31-
* specified. For live playlists, it should be the difference
32-
* between the media sequence numbers in the original and updated
33-
* playlists.
34-
* @return a list of merged segment objects
35-
*/
22+
* Returns a new segment object with properties and
23+
* the parts array merged.
24+
*
25+
* @param {Object} a the old segment
26+
* @param {Object} b the new segment
27+
*
28+
* @return {Object} the merged segment
29+
*/
30+
export const updateSegment = (a, b) => {
31+
if (!a) {
32+
return b;
33+
}
34+
35+
const result = mergeOptions(a, b);
36+
37+
// if only the old segment has parts
38+
// then the parts are no longer valid
39+
if (a.parts && !b.parts) {
40+
delete result.parts;
41+
// if both segments have parts
42+
// copy part propeties from the old segment
43+
// to the new one.
44+
} else if (a.parts && b.parts) {
45+
for (let i = 0; i < b.parts.length; i++) {
46+
if (a.parts && a.parts[i]) {
47+
result.parts[i] = mergeOptions(a.parts[i], b.parts[i]);
48+
}
49+
}
50+
}
51+
52+
return result;
53+
};
54+
55+
/**
56+
* Returns a new array of segments that is the result of merging
57+
* properties from an older list of segments onto an updated
58+
* list. No properties on the updated playlist will be ovewritten.
59+
*
60+
* @param {Array} original the outdated list of segments
61+
* @param {Array} update the updated list of segments
62+
* @param {number=} offset the index of the first update
63+
* segment in the original segment list. For non-live playlists,
64+
* this should always be zero and does not need to be
65+
* specified. For live playlists, it should be the difference
66+
* between the media sequence numbers in the original and updated
67+
* playlists.
68+
* @return {Array} a list of merged segment objects
69+
*/
3670
export const updateSegments = (original, update, offset) => {
71+
const oldSegments = original.slice();
3772
const result = update.slice();
3873

3974
offset = offset || 0;
4075
const length = Math.min(original.length, update.length + offset);
4176

4277
for (let i = offset; i < length; i++) {
43-
result[i - offset] = mergeOptions(original[i], result[i - offset]);
78+
const newIndex = i - offset;
79+
80+
result[newIndex] = updateSegment(oldSegments[i], result[newIndex]);
4481
}
4582
return result;
4683
};
4784

4885
export const resolveSegmentUris = (segment, baseUri) => {
49-
if (!segment.resolvedUri) {
86+
// preloadSegments will not have a uri at all
87+
// as the segment isn't actually in the manifest yet, only parts
88+
if (!segment.resolvedUri && segment.uri) {
5089
segment.resolvedUri = resolveUrl(baseUri, segment.uri);
5190
}
5291
if (segment.key && !segment.key.resolvedUri) {
@@ -55,6 +94,23 @@ export const resolveSegmentUris = (segment, baseUri) => {
5594
if (segment.map && !segment.map.resolvedUri) {
5695
segment.map.resolvedUri = resolveUrl(baseUri, segment.map.uri);
5796
}
97+
if (segment.parts && segment.parts.length) {
98+
segment.parts.forEach((p) => {
99+
if (p.resolvedUri) {
100+
return;
101+
}
102+
p.resolvedUri = resolveUrl(baseUri, p.uri);
103+
});
104+
}
105+
106+
if (segment.preloadHints && segment.preloadHints.length) {
107+
segment.preloadHints.forEach((p) => {
108+
if (p.resolvedUri) {
109+
return;
110+
}
111+
p.resolvedUri = resolveUrl(baseUri, p.uri);
112+
});
113+
}
58114
};
59115

60116
// consider the playlist unchanged if the playlist object is the same or
@@ -173,6 +229,7 @@ export default class PlaylistLoader extends EventTarget {
173229

174230
this.customTagParsers = (vhsOptions && vhsOptions.customTagParsers) || [];
175231
this.customTagMappers = (vhsOptions && vhsOptions.customTagMappers) || [];
232+
this.experimentalLLHLS = (vhsOptions && vhsOptions.experimentalLLHLS) || false;
176233

177234
// initialize the loader state
178235
this.state = 'HAVE_NOTHING';
@@ -254,7 +311,8 @@ export default class PlaylistLoader extends EventTarget {
254311
oninfo: ({message}) => this.logger_(`m3u8-parser info for ${id}: ${message}`),
255312
manifestString: playlistString,
256313
customTagParsers: this.customTagParsers,
257-
customTagMappers: this.customTagMappers
314+
customTagMappers: this.customTagMappers,
315+
experimentalLLHLS: this.experimentalLLHLS
258316
});
259317

260318
playlist.lastRequest = Date.now();
@@ -562,7 +620,8 @@ export default class PlaylistLoader extends EventTarget {
562620
const manifest = parseManifest({
563621
manifestString: req.responseText,
564622
customTagParsers: this.customTagParsers,
565-
customTagMappers: this.customTagMappers
623+
customTagMappers: this.customTagMappers,
624+
llhls: this.llhls
566625
});
567626

568627
this.setupInitialPlaylist(manifest);

0 commit comments

Comments
 (0)