Skip to content

Commit

Permalink
feat: Add ll-hls ext-x-part support behind a flag
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey committed Feb 22, 2021
1 parent 327a572 commit 1523e72
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 37 deletions.
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ <h3>Options</h3>
<input id=partial type="checkbox">
Handle Partial (reloads player)
</label>
<label>
<input id=llhls type="checkbox">
[EXPERIMENTAL] Enables support for ll-hls (reloads player)
</label>
<label>
<input id=buffer-water type="checkbox">
[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)
Expand Down
11 changes: 10 additions & 1 deletion scripts/index-demo-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@
'sync-workers',
'liveui',
'partial',
'llhls',
'url',
'type',
'keysystems',
Expand Down Expand Up @@ -292,6 +293,13 @@
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
});

stateEls.llhls.addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls.partial.addEventListener('change', function(event) {
saveState();

Expand Down Expand Up @@ -369,7 +377,8 @@
vhs: {
overrideNative: getInputValue(stateEls['override-native']),
handlePartialData: getInputValue(stateEls.partial),
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water'])
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
llhls: getInputValue(stateEls.llhls)
}
}
});
Expand Down
33 changes: 31 additions & 2 deletions src/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const parseManifest = ({
oninfo,
manifestString,
customTagParsers = [],
customTagMappers = []
customTagMappers = [],
llhls
}) => {
const parser = new M3u8Parser();

Expand All @@ -43,7 +44,35 @@ export const parseManifest = ({
parser.push(manifestString);
parser.end();

return parser.manifest;
const manifest = parser.manifest;

// remove llhls features from the parsed manifest
if (!llhls) {
[
'preloadSegment',
'skip',
'serverControl',
'renditionReports',
'partInf',
'partTargetDuration'
].forEach(function(k) {
if (manifest.hasOwnProperty(k)) {
delete manifest[k];
}
});

if (manifest.segments) {
manifest.segments.forEach(function(segment) {
['parts', 'preloadHints'].forEach(function(k) {
if (segment.hasOwnProperty(k)) {
delete segment[k];
}
});
});
}
}

return manifest;
};

/**
Expand Down
2 changes: 1 addition & 1 deletion src/media-segment-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ export const mediaSegmentRequest = ({
}

const segmentRequestOptions = videojs.mergeOptions(xhrOptions, {
uri: segment.resolvedUri,
uri: segment.part && segment.part.resolvedUri || segment.resolvedUri,
responseType: 'arraybuffer',
headers: segmentXhrHeaders(segment)
});
Expand Down
45 changes: 41 additions & 4 deletions src/playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,36 @@ const { mergeOptions, EventTarget } = videojs;
* @return a list of merged segment objects
*/
export const updateSegments = (original, update, offset) => {
const oldSegments = original.slice();
const result = update.slice();

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

for (let i = offset; i < length; i++) {
result[i - offset] = mergeOptions(original[i], result[i - offset]);
const newIndex = i - offset;

// merge parts
if (result[newIndex] && result[newIndex].parts && oldSegments[i] && oldSegments[i].parts) {
for (let p = 0; p < result[newIndex].parts; p++) {
result[newIndex].parts[p] = mergeOptions(oldSegments[i].parts[p], result[newIndex].parts[p]);
}
}

// part merging happens above. If the new playlist has no parts for
// a segment, they are no longer valid for requsting
if (oldSegments[i] && oldSegments[i].parts) {
delete oldSegments[i].parts;
}
result[newIndex] = mergeOptions(original[i], result[i - offset]);
}
return result;
};

export const resolveSegmentUris = (segment, baseUri) => {
if (!segment.resolvedUri) {
// preloadSegments will not have a uri at all
// as the segment isn't actually in the manifest yet, only parts
if (!segment.resolvedUri && segment.uri) {
segment.resolvedUri = resolveUrl(baseUri, segment.uri);
}
if (segment.key && !segment.key.resolvedUri) {
Expand All @@ -55,6 +72,23 @@ export const resolveSegmentUris = (segment, baseUri) => {
if (segment.map && !segment.map.resolvedUri) {
segment.map.resolvedUri = resolveUrl(baseUri, segment.map.uri);
}
if (segment.parts && segment.parts.length) {
segment.parts.forEach((p) => {
if (p.resolvedUri) {
return;
}
p.resolvedUri = resolveUrl(baseUri, p.URI);
});
}

if (segment.preloadHints && segment.preloadHints.length) {
segment.preloadHints.forEach((p) => {
if (p.resolvedUri) {
return;
}
p.resolvedUri = resolveUrl(baseUri, p.URI);
});
}
};

// consider the playlist unchanged if the playlist object is the same or
Expand Down Expand Up @@ -173,6 +207,7 @@ export default class PlaylistLoader extends EventTarget {

this.customTagParsers = (vhsOptions && vhsOptions.customTagParsers) || [];
this.customTagMappers = (vhsOptions && vhsOptions.customTagMappers) || [];
this.llhls = (vhsOptions && vhsOptions.llhls) || false;

// initialize the loader state
this.state = 'HAVE_NOTHING';
Expand Down Expand Up @@ -254,7 +289,8 @@ export default class PlaylistLoader extends EventTarget {
oninfo: ({message}) => this.logger_(`m3u8-parser info for ${id}: ${message}`),
manifestString: playlistString,
customTagParsers: this.customTagParsers,
customTagMappers: this.customTagMappers
customTagMappers: this.customTagMappers,
llhls: this.llhls
});

playlist.lastRequest = Date.now();
Expand Down Expand Up @@ -562,7 +598,8 @@ export default class PlaylistLoader extends EventTarget {
const manifest = parseManifest({
manifestString: req.responseText,
customTagParsers: this.customTagParsers,
customTagMappers: this.customTagMappers
customTagMappers: this.customTagMappers,
llhls: this.llhls
});

this.setupInitialPlaylist(manifest);
Expand Down
Loading

0 comments on commit 1523e72

Please sign in to comment.