-
Notifications
You must be signed in to change notification settings - Fork 426
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 serverControl and preloadSegment llhls features behind a flag #1078
Conversation
src/master-playlist-controller.js
Outdated
|
||
if (this.masterPlaylistLoader_.master.hasOwnProperty('suggestedPresentationDelay')) { | ||
delay = this.masterPlaylistLoader_.master.suggestedPresentationDelay; | ||
} else if (media.serverControl && media.serverControl['PART-HOLD-BACK'] && media.partTargetDuration) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ll-hls has it's own version of when a playlist should be started called HOLD-BACK
and PART-HOLD-BACK
src/master-playlist-controller.js
Outdated
} else if (media.partTargetDuration) { | ||
delay = media.partTargetDuration * 3; | ||
} else if (media.targetDuration) { | ||
delay = media.targetDuration * 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the default values for HOLD-BACK
as per the spec, so even if a playlist doesn't have HOLD-BACK
we can still set the default HOLD-BACK
src/playlist-loader.js
Outdated
// if the playlist is unchanged since the last reload or last segment duration | ||
// cannot be determined, try again after half the target duration | ||
delay = (media.targetDuration || 10) * 500; | ||
const lastPart = lastSegment && lastSegment.parts && lastSegment.parts[lastSegment.parts - 1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the last part duration if it exists or the last segment duration
|
||
// if the playlist is unchanged since the last reload or last segment duration | ||
// cannot be determined, try again after half the target duration | ||
return (media.partTargetDuration || media.targetDuration || 10) * 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use partTargetDuration or targetDuration
@@ -304,7 +313,7 @@ export default class PlaylistLoader extends EventTarget { | |||
// merge this playlist into the master | |||
const update = updateMaster(this.master, playlist); | |||
|
|||
this.targetDuration = playlist.targetDuration; | |||
this.targetDuration = playlist.partTargetDuration || playlist.targetDuration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use a playlists targetDuration in a few places outside of these files. In the case where we have partTargetDuration that is always what we will use as out "true" targetDuration, I understand that this is ambiguous though and I think we might want to rename this.targetDuration
to something else, but that might be a breaking change. Should we deprecate this name?
src/playlist.js
Outdated
let segment; | ||
const numSegments = playlist.segments.length; | ||
|
||
const partSegments = playlist.segments.reduce((acc, segment, si) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this function to know about parts now. So we break put all of our segments and or parts into one list.
ec781dc
to
6090bb5
Compare
timeline | ||
} = segmentInfo; | ||
|
||
const name = segmentInfo.segment.uri ? 'segment' : 'pre-segment'; | ||
|
||
return [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better logging in general, but especially needed now that we have parts.
const mediaSourceInfo = Playlist.getMediaInfoForTime( | ||
playlist, | ||
currentTime, | ||
this.fetchAtBuffer_ ? lastBufferedEnd : currentTime, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was silly to have 10 lines duplicated for a single argument change.
nextPartIndex = mediaSourceInfo.partIndex; | ||
} | ||
|
||
if (typeof nextPartIndex !== 'number' && playlist.segments[nextMediaIndex] && playlist.segments[nextMediaIndex].parts) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we don't have a nextPartIndex, check if we should.
6090bb5
to
c3951b2
Compare
eeba482
to
1161c6f
Compare
1b45396
to
e9704a3
Compare
e9704a3
to
a8957ff
Compare
1161c6f
to
3309677
Compare
3309677
to
37ae651
Compare
Codecov Report
@@ Coverage Diff @@
## main #1078 +/- ##
==========================================
+ Coverage 86.06% 86.13% +0.06%
==========================================
Files 38 38
Lines 8923 8967 +44
Branches 1998 2020 +22
==========================================
+ Hits 7680 7724 +44
Misses 1243 1243
Continue to review full report at Codecov.
|
// dash suggestedPresentationDelay trumps everything | ||
if (master && master.suggestedPresentationDelay) { | ||
return master.suggestedPresentationDelay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth pulling this up to short circuit the rest of the function.
@@ -1352,41 +1360,36 @@ export default class SegmentLoader extends videojs.EventTarget { | |||
} else if (currentMediaIndex !== null) { | |||
// Under normal playback conditions fetching is a simple walk forward | |||
const segment = playlist.segments[currentMediaIndex]; | |||
const partIndex = typeof currentPartIndex === 'number' ? currentPartIndex : -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for it to be a valid case where the currentMediaIndex
is the segment we're supposed to be fetching will the partIndex
is 0? Are the hinted segments available here to see?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currentPartIndex will come in as null, in which case we need to check if the current media index has parts at index 0. it could also come in as 0 and in that case we know we appended part 0 and need to look for part 1, or increment media index if we don't have it.
src/segment-loader.js
Outdated
startOfSegment, | ||
isSyncRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can actually be valuable to contain, in particular startOfSegment
, which has been a source of various bugs we've encountered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all on segmentInfo, which is my reasoning behind removing them. But we could keep them as they keep things a bit more relevant to the checkBuffer log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these get logged except for here. Maybe worth adding to segmentInfoString
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the next pull request in the series (where I refactor our segment selection logic) that adds selection to segmentInfoString
. I think we should wait until that pull request. I will revert this logging change here for now.
test/playlist.test.js
Outdated
2, | ||
'uses last three part durations' | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want another test here with fewer than 3 parts available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in a real scenario the last 3 segments will always have >= 2 parts. as having only one part is the same as not using parts at all. I will update the test for that.
src/playlist.js
Outdated
// we shouldn't ever end up using lastThreeDurations as targetDuration | ||
// is usually required, but if we somehow get here, with a missing | ||
// targetDuration we should handle it | ||
} else if (lastThreeDurations) { | ||
return lastThreeDurations; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we shouldn't end up here, it's probably worth pulling all of that logic down into an else
here and return either 0 or whatever we calculate as the last three. Might also be worth warning (if we don't warn elsewhere for missing target duration), or logging anyway to help us determine what's happening in the event of a missing target duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use lastThreeDurations in the case where we have parts, but no partTargetDuration so that we get a more accurate hold back for ll-hls. We warn about target duration via the manifest parser warnings that now go to the debug log.
src/playlist.js
Outdated
// by default we use the last three durations of segments if | ||
// part target duration or target duration isn't found. | ||
// see: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-4.4.3.8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be missing something, but I don't think that section of the spec, if referring to HOLD-BACK
, matches the comment here, since that is saying to use part target duration or target duration when the parameter is not available, and doesn't refer to the use of segment durations. Do we just have this in case the required target durations are missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we use three segment durations in case target durations are not available for whatever reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it's worth it to add the logic for this here if those are required attributes. It adds logic to the code that we shouldn't really encounter, and if we were to, then it's something that should be resolved by the creator of the stream, since target duration is always required (https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-4.4.3.1) and part target duration is required when parts are used (https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-4.4.3.7).
I think if we're trying to account for missing target durations we should do that at a higher level, when we first parse the manifest. It would help to simplify the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I will remove the last three durations logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just default to 10s target duration in case somehow it isn't set or whatever? While I agree that bad streams are bad, we should still try to play as much as possible and having a default, if bad value, is worth it. Plus, it should make things simpler than trying to figure out segment duration and using that for target duation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having a sort of default may be appropriate (along with a warning). We should probably add it at the point where we first parse the stream (i.e., in the loaders).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I don't see any major issues, I think there are a couple of small changes that should be made in playlist.js based on Garrett's comments. Would make that function slightly simpler.
src/playlist.js
Outdated
// by default we use the last three durations of segments if | ||
// part target duration or target duration isn't found. | ||
// see: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-4.4.3.8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about just default to 10s target duration in case somehow it isn't set or whatever? While I agree that bad streams are bad, we should still try to play as much as possible and having a default, if bad value, is worth it. Plus, it should make things simpler than trying to figure out segment duration and using that for target duation.
Also, this PR is now out of date with main. |
src/playlist.js
Outdated
// by default we use the last three durations of segments if | ||
// part target duration or target duration isn't found. | ||
// see: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-4.4.3.8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having a sort of default may be appropriate (along with a warning). We should probably add it at the point where we first parse the stream (i.e., in the loaders).
src/manifest.js
Outdated
const lastDuration = lastSegment && Math.round(lastSegment) || 10; | ||
|
||
if (onwarn) { | ||
onwarn(`manifest has no targetDuration defaulting to ${lastDuration}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may be better off using the largest segment duration found (and 10 if no segments included), since the TARGETDURATION is supposed to be the highest duration that will be encountered in a stream (and we will run into issues if the target duration is shorter than some segment durations).
Would also be worth having a test for it.
Co-authored-by: Garrett Singer <[email protected]>
const lastPartDuration = lastSegment && lastSegment.duration || media.targetDuration; | ||
|
||
return lastPartDuration + media.targetDuration * 2; | ||
return media.targetDuration * 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was changed in main via #1105
Description
llhls features are already behind a flag, from the previous pull request #1055.
This implements usage of:
preloadSegment
when it has parts. Think of this as the segment that is about to appear in the manifest, but we don't have a uri for yet.severControl
but onlyPART-HOLD-BACK
andHOLD-BACK
which specify how far behind a live playlist we should startpartTargetDuration
in any way that we current use targetDuration, such as how much of a delay we should have on a playlist refresh