Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use EXT-X-PART for LL-HLS #1055

Merged
merged 12 commits into from
Mar 19, 2021
Merged

feat: use EXT-X-PART for LL-HLS #1055

merged 12 commits into from
Mar 19, 2021

Conversation

brandonocasey
Copy link
Contributor

@brandonocasey brandonocasey commented Jan 27, 2021

Description

Implements support for EXT-X-PART and EXT-X-PRELOAD-HINT
Requires videojs/m3u8-parser#137

Future pull requests

Investigation

  • How would we use #EXT-X-RENDITION-REPORT when switching renditions? Is it only relevant in the context of delta updates?

@brandonocasey brandonocasey changed the title WIP: use EXT-X-PART EXT-X-PRELOAD-HINT for LL-HLS WIP: use EXT-X-PART for LL-HLS Feb 4, 2021
@brandonocasey brandonocasey changed the title WIP: use EXT-X-PART for LL-HLS feat: use EXT-X-PART for LL-HLS Feb 10, 2021
@brandonocasey brandonocasey force-pushed the poc-ll-hls branch 2 times, most recently from 55ad96c to 1523e72 Compare February 22, 2021 20:25
@@ -971,7 +971,7 @@ export const mediaSegmentRequest = ({
}

const segmentRequestOptions = videojs.mergeOptions(xhrOptions, {
uri: segment.resolvedUri,
uri: segment.part && segment.part.resolvedUri || segment.resolvedUri,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't actually use uri anywhere 🤷 but for completeness sake I changed it.

// In that case we need to reset partIndex and resync
if (this.partIndex && (!segment.parts || !segment.parts.length || !segment.parts[this.partIndex])) {
this.logger_(`part fell off on part ${this.partIndex}`);
this.resetLoader();
Copy link
Contributor Author

@brandonocasey brandonocasey Feb 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure of a better way to do this. If we appended part 4 of 8 but parts fell off the manifest, they will no longer be request-able, so we kind of have to reset partIndex and the buffer. Perhaps the solution is to never use the first sequence of parts in a manifest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may be OK since:

EXT-X-PART tags SHOULD be removed from the Playlist after they are
greater than three Target Durations from the end of the Playlist.
https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.2.2

I imagine if we are close to the live edge we shouldn't fall that far back, and if we do, it may be worth doing something else (like seeking to live, or just reverting to standard playback which should happen with the reset).

@@ -470,15 +475,23 @@ export const playlistWithDuration = function(time, conf) {
segment.discontinuity = true;
}

// add parts for the the last 3 segments in llhls playlists
if (conf && conf.llhls && (count - i) <= 3) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add parts if llhls is set.

const remainder = time % targetDuration;
const count = Math.floor(time / targetDuration) + (remainder ? 1 : 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't append remainder after the loop, just do it in the for loop.

src/manifest.js Outdated Show resolved Hide resolved
src/manifest.js Show resolved Hide resolved
src/playlist-loader.js Outdated Show resolved Hide resolved
src/playlist-loader.js Outdated Show resolved Hide resolved
src/playlist-loader.js Outdated Show resolved Hide resolved
// In that case we need to reset partIndex and resync
if (this.partIndex && (!segment.parts || !segment.parts.length || !segment.parts[this.partIndex])) {
this.logger_(`part fell off on part ${this.partIndex}`);
this.resetLoader();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may be OK since:

EXT-X-PART tags SHOULD be removed from the Playlist after they are
greater than three Target Durations from the end of the Playlist.
https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.2.2

I imagine if we are close to the live edge we shouldn't fall that far back, and if we do, it may be worth doing something else (like seeking to live, or just reverting to standard playback which should happen with the reset).

src/segment-loader.js Outdated Show resolved Hide resolved
test/test-helpers.js Outdated Show resolved Hide resolved
test/loader-common.js Outdated Show resolved Hide resolved
test/loader-common.js Outdated Show resolved Hide resolved
scripts/index-demo-page.js Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Mar 12, 2021

Codecov Report

Merging #1055 (74c785f) into main (87947fc) will decrease coverage by 0.06%.
The diff coverage is 81.60%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1055      +/-   ##
==========================================
- Coverage   86.17%   86.10%   -0.07%     
==========================================
  Files          38       38              
  Lines        8838     8905      +67     
  Branches     1965     1996      +31     
==========================================
+ Hits         7616     7668      +52     
- Misses       1222     1237      +15     
Impacted Files Coverage Δ
src/videojs-http-streaming.js 90.58% <ø> (ø)
src/playlist-loader.js 91.16% <53.84%> (-4.40%) ⬇️
src/manifest.js 97.43% <84.61%> (-2.57%) ⬇️
src/segment-loader.js 95.30% <95.74%> (-0.06%) ⬇️
src/media-segment-request.js 95.73% <100.00%> (+0.01%) ⬆️
src/source-updater.js 94.83% <0.00%> (+0.32%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 87947fc...74c785f. Read the comment docs.

src/manifest.js Outdated Show resolved Hide resolved
// this can happen if we are going to load the first segment, but get a playlist
// update during that. mediaIndex would go from 0 to -1 if mediaSequence in the
// new playlist was incremented by 1.
if (this.mediaIndex < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this will get us into trouble for short live playlists. Since we should start at the beginning of the playlist, the case may often happen that we end up with a mediaIndex of -1 while still processing the segment. In that case, the next segment we should load is segment 0 from the new playlist, which, if we do mediaIndex++, we'll be OK, unless we are at -2 or below. That may be a case for us to do a resync or seek.

Do other areas of the code have trouble with a mediaIndex of -1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we start at the end for live playlists?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason that I added this code was:

  1. I have often seen issues with background tabs and live playlist refreshes happing way too slowly, so the segment we are on falls off of the playlist.
  2. having a negative media index doesn't really make sense, as we are dealing with zero based arrays. If the segment we are on falls off of the current playlist then the current media index would no longer be a valid media index for referring to the segment that we have appended.

I think ultimately the difference will be that we will never have a negative mediaIndex. So if we were two segments behind mediaIndex would not become -2 and leave us in a bad state. It would become negative and we would get the currentMediaIndex for time.

It's possible that when mediaIndex and pendingSegment are both null, that we should probably try to jump closer to the live point in the stream. Right now we will just keep trying to request a segment at mediaIndex -1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking again, I think we may be OK. My concern was mainly for the case of 3 segment live playlists. We start at the beginning of the first segment in the playlist, and it will probably be pretty often that we'll update our playlist during a segment request. If that happens, we clear out mediaIndex here (since the mediaIndex was 0 and we'd go to -1), so when we get to checkBuffer for the next segment, we'd skip past mediaIndex++, but it looks like we'd still fetch at the end of buffer, so the guess should be OK:

} else if (this.fetchAtBuffer_) {
// Find the segment containing the end of the buffer
const mediaSourceInfo = Playlist.getMediaInfoForTime(
playlist,
lastBufferedEnd,
syncPoint.segmentIndex,
syncPoint.time
);
nextMediaIndex = mediaSourceInfo.mediaIndex;
startOfSegment = mediaSourceInfo.startTime;

src/playlist-loader.js Outdated Show resolved Hide resolved
src/playlist-loader.js Outdated Show resolved Hide resolved
this.logger_(`currently processing part (index ${this.partIndex}) no longer exists.`);
this.resetLoader();

this.mediaIndex = mediaIndex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that we retain the media index after a reset?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to throw away the partIndex and the data associated with it, but the mediaIndex is still valid.
Example:
We are on mediaIndex 3 and partIndex 2, then a live refresh happens and we are on mediaIndex 2 and it no longer has parts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 would be worth a comment there

@brandonocasey brandonocasey merged commit b33e109 into main Mar 19, 2021
@brandonocasey brandonocasey deleted the poc-ll-hls branch March 19, 2021 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants