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

fix: more accurate segment choices and logging #1127

Merged
merged 14 commits into from
May 26, 2021

Conversation

brandonocasey
Copy link
Contributor

Description

This pull request refactors two functions that are the cause of latency drift in live streams during rendition changes. It also vastly improves the segmentInfoString logging logic.

SegmentLoader#getSyncSegmentCandidate

What we do right now is choose a segment that will never be the one we want, still append it to the source buffer and then ignore the final "end result" (saving the media/part index as the current one).

I have improved this logic in the following ways:

  1. Try to choose an accurate segment that will be appended
  2. If the segment isn't accurate, save the timing info but don't append saving us a lot of source buffer latency.
  3. If the segment is accurate append it and save it's media/part index as the current media/part index.

Playlist#getMediaInfoForTime

Currently we add TIME_FUDGE_FACTOR for each segment and this causes us to choose the wrong segment more often then not for playlists with > 15 segments as we will be off by more than half a second in those cases. Instead we should only add TIME_FUDGE_FACTOR a single time when comparing against the time we want. We might even want to consider not using TIME_FUDGE_FACTOR here.

@codecov
Copy link

codecov bot commented May 5, 2021

Codecov Report

Merging #1127 (1d74f5d) into main (1c7a63b) will decrease coverage by 0.07%.
The diff coverage is 86.48%.

❗ Current head 1d74f5d differs from pull request most recent head 504f434. Consider uploading reports for the commit 504f434 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1127      +/-   ##
==========================================
- Coverage   86.20%   86.13%   -0.08%     
==========================================
  Files          39       39              
  Lines        9289     9272      -17     
  Branches     2127     2123       -4     
==========================================
- Hits         8008     7986      -22     
- Misses       1281     1286       +5     
Impacted Files Coverage Δ
src/segment-loader.js 95.39% <84.84%> (-0.42%) ⬇️
src/playlist.js 94.50% <100.00%> (ø)
src/playlist-loader.js 91.40% <0.00%> (-0.23%) ⬇️
src/manifest.js 97.67% <0.00%> (-0.18%) ⬇️
src/media-groups.js 98.90% <0.00%> (-0.07%) ⬇️

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 458be2c...504f434. Read the comment docs.


if (time > 0) {
if ((time + TIME_FUDGE_FACTOR) > 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.

Should we keep TIME_FUDGE_FACTOR at all?

Copy link
Member

Choose a reason for hiding this comment

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

question: What is TIME_FUDGE_FACTOR used for here? Is using it causing timing issues down the line?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this may be the wrong place for us to use time fudging. We should probably only ever fudge time in one place: when we are trying to determine the next segment to request and are not simply walking the playlist (mediaIndex++). In that case, all requests to get media info/segment info should be as precise as possible given the current segment information, then, once the final segment is determined, we should decide whether we want to fudge the time at all to conservatively request a segment (i.e., whether to request back 1 segment, or, as an alternative, get media info for time with a fudge factor included).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this change basically encompasses what you are saying. We only use getMediaIndexForTime when we are not incrementing mediaIndex/partIndex. This change makes it so that cumulatively we can only be off by TIME_FUDGE_FACTOR rather than every single part/segments duration being incremented or decremented by TIME_FUDGE_FACTOR. For the sake of time I think that we might want to add a TODO here to see if we should even use TIME_FUDGE_FACTOR at all. I lean towards not using it, but it will require a bit more testing and could easily enough be an isolated change.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 a TODO would be good for now. This definitely is better than before.

Copy link
Member

Choose a reason for hiding this comment

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

Probably worth making an issue/a note in our major/refactoring epic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

@@ -870,7 +870,7 @@ export const LoaderCommonFactory = ({
});

QUnit.test('drops partIndex if playlist update drops parts', function(assert) {
assert.timeout(100000000000000000000);
loader.duration_ = () => Infinity;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ie 11 has the duration as NaN here, so sync-controller gets a sync point of 1 for a vod video.

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

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


if (time > 0) {
if ((time + TIME_FUDGE_FACTOR) > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

question: What is TIME_FUDGE_FACTOR used for here? Is using it causing timing issues down the line?

src/segment-loader.js Outdated Show resolved Hide resolved
src/segment-loader.js Outdated Show resolved Hide resolved
@@ -2967,6 +2954,65 @@ QUnit.module('SegmentLoader', function(hooks) {
});
});

QUnit.test('sync request can be thrown away', function(assert) {
Copy link
Member

Choose a reason for hiding this comment

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


if (time > 0) {
if ((time + TIME_FUDGE_FACTOR) > 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 think this may be the wrong place for us to use time fudging. We should probably only ever fudge time in one place: when we are trying to determine the next segment to request and are not simply walking the playlist (mediaIndex++). In that case, all requests to get media info/segment info should be as precise as possible given the current segment information, then, once the final segment is determined, we should decide whether we want to fudge the time at all to conservatively request a segment (i.e., whether to request back 1 segment, or, as an alternative, get media info for time with a fudge factor included).

src/segment-loader.js Outdated Show resolved Hide resolved
src/segment-loader.js Outdated Show resolved Hide resolved
src/segment-loader.js Outdated Show resolved Hide resolved
// throw away the isSyncRequest segment if
// it wouldn't have been the next segment we request.
if (segmentInfo.isSyncRequest) {
this.syncController_.saveSegmentTimingInfo({
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 have to be careful here, since we're losing some of our timing logic, which may lead to inaccurate next requests:

// Now that the end of the segment has been reached, we can set the end time. It's
// best to wait until all appends are done so we're sure that the primary media is
// finished (and we have its end time).
this.updateTimingInfoEnd_(segmentInfo);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we call this function within this block? From a brief glance it seems like it only estimates the segment end point using start/duration unless we have an end. Seems like we should call it here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should probably call it. On that note we may also want to consider adding a property to the segment whether the last time we requested that segment it was a sync request (and clear it if it's requested on a non sync request). It may help us for debugging, particularly now that it's no longer being appended.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

called

test/segment-loader.test.js Outdated Show resolved Hide resolved
test/segment-loader.test.js Outdated Show resolved Hide resolved
src/playlist.js Outdated Show resolved Hide resolved
gesinger
gesinger previously approved these changes May 26, 2021

return `${name} [${seq + index}/${seq + segmentLen}]` +
(hasPartIndex ? ` part [${partIndex}/${zeroBasedPartCount}]` : '') +
(hasPartIndex ? ` part [${partIndex}/${totalParts}]` : '') +
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this take into account the 0 based indexing vs total. Maybe we can just say `[index 0, 3 total]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should keep the name zeroBasedPartCount here I guess. I do subtract 1 to make it zero based.

@brandonocasey brandonocasey merged commit ce03f66 into main May 26, 2021
@brandonocasey brandonocasey deleted the fix/better-segment-choice branch May 26, 2021 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants