diff options
author | James Taylor <user234683@users.noreply.github.com> | 2021-09-01 17:32:18 -0700 |
---|---|---|
committer | Jesús <heckyel@hyperbola.info> | 2021-09-01 20:12:14 -0500 |
commit | fc0fa9aaba9caa0b849a91f1c989e52ab4a7ca15 (patch) | |
tree | 8072e39d06422a4b5975f37435690148f9e9b55b /youtube | |
parent | 06e091e020a2fecd1ef927edc4f55e999804b5f0 (diff) | |
download | yt-local-fc0fa9aaba9caa0b849a91f1c989e52ab4a7ca15.tar.lz yt-local-fc0fa9aaba9caa0b849a91f1c989e52ab4a7ca15.tar.xz yt-local-fc0fa9aaba9caa0b849a91f1c989e52ab4a7ca15.zip |
av-merge: Fix segments not properly reappended during QuotaExceeded
Two issues fixed:
1. The append was only retried if it was the result of a seek
event. But if the video is paused (such as if the video was
finished and the user seeks back to the beginning), the seek
won't happen because the MediaSource will not issue a sourceopen
until the user plays the video. A better strategy that solves
the true issue is to retry the append if it is for the segment
corresponding to the current time, since that is critical to get
immediately.
2. If the append was not retried, entry.requested was not getting
marked as false, so it would refuse to ever rerequest the segment,
so it would stall. Set it to false if we decide not to retry the
append, so it can be rerequested later.
Signed-off-by: Jesús <heckyel@hyperbola.info>
Diffstat (limited to 'youtube')
-rw-r--r-- | youtube/static/js/av-merge.js | 39 |
1 files changed, 23 insertions, 16 deletions
diff --git a/youtube/static/js/av-merge.js b/youtube/static/js/av-merge.js index dfbf3c9..6a5e044 100644 --- a/youtube/static/js/av-merge.js +++ b/youtube/static/js/av-merge.js @@ -166,7 +166,7 @@ function Stream(avMerge, source, startTime, avRatio) { this.mediaSource = avMerge.mediaSource; this.sidx = null; this.appendRetries = 0; - this.appendQueue = []; // list of [segmentIdx, forSeek, data] + this.appendQueue = []; // list of [segmentIdx, data] this.sourceBuffer = this.mediaSource.addSourceBuffer(this.mimeCodec); this.sourceBuffer.mode = 'segments'; this.sourceBuffer.addEventListener('error', (e) => { @@ -189,7 +189,7 @@ Stream.prototype.setup = async function(){ var init_end = this.initRange.end - this.initRange.start + 1; var index_start = this.indexRange.start - this.initRange.start; var index_end = this.indexRange.end - this.initRange.start + 1; - this.appendSegment(null, false, buffer.slice(0, init_end)); + this.appendSegment(null, buffer.slice(0, init_end)); this.setupSegments(buffer.slice(index_start, index_end)); } ) @@ -199,7 +199,7 @@ Stream.prototype.setup = async function(){ this.url, this.initRange.start, this.initRange.end, - this.appendSegment.bind(this, false, null), + this.appendSegment.bind(this, null), ); // sidx (segment index) table fetchRange( @@ -224,7 +224,7 @@ Stream.prototype.close = function() { this.mediaSource.removeSourceBuffer(this.sourceBuffer); this.updateendEvt.remove(); } -Stream.prototype.appendSegment = function(segmentIdx, forSeek, chunk) { +Stream.prototype.appendSegment = function(segmentIdx, chunk) { if (this.closed) return; @@ -233,7 +233,7 @@ Stream.prototype.appendSegment = function(segmentIdx, forSeek, chunk) { // cannot append right now, schedule for updateend if (this.sourceBuffer.updating) { this.reportDebug('sourceBuffer updating, queueing for later'); - this.appendQueue.push([segmentIdx, forSeek, chunk]); + this.appendQueue.push([segmentIdx, chunk]); if (this.appendQueue.length > 2){ this.reportWarning('appendQueue length:', this.appendQueue.length); } @@ -267,25 +267,26 @@ Stream.prototype.appendSegment = function(segmentIdx, forSeek, chunk) { this.reportDebug('New buffer target:', this.bufferTarget); } - // Delete 3 segments (arbitrary) from buffer, making sure + // Delete 10 segments (arbitrary) from buffer, making sure // not to delete current one var currentSegment = this.getSegmentIdx(this.video.currentTime); var numDeleted = 0; var i = 0; + const DELETION_TARGET = 10; var toDelete = []; // See below for why we have to schedule it this.reportDebug('Deleting segments from beginning of buffer.'); - while (numDeleted < 3 && i < currentSegment) { + while (numDeleted < DELETION_TARGET && i < currentSegment) { if (this.sidx.entries[i].have) { toDelete.push(i) numDeleted++; } i++; } - if (numDeleted < 3) + if (numDeleted < DELETION_TARGET) this.reportDebug('Deleting segments from end of buffer.'); i = this.sidx.entries.length - 1; - while (numDeleted < 3 && i > currentSegment) { + while (numDeleted < DELETION_TARGET && i > currentSegment) { if (this.sidx.entries[i].have) { toDelete.push(i) numDeleted++; @@ -297,15 +298,21 @@ Stream.prototype.appendSegment = function(segmentIdx, forSeek, chunk) { // state, and remove cannot be called until it is done. So we have // to delete on the updateend event for subsequent ones. var removeFinishedEvent; + var deletedStuff = (toDelete.length !== 0) var deleteSegment = () => { if (toDelete.length === 0) { - // If QuotaExceeded happened during seeking, retry the append - // Pass false as forSeek to avoid infinite looping if it - // doesn't work. Rescheduling will take care of updating=true - // problem. removeFinishedEvent.remove(); - if (forSeek) { - this.appendSegment(segmentIdx, false, chunk); + // If QuotaExceeded happened for current segment, retry the + // append + // Rescheduling will take care of updating=true problem. + // Also check that we found segments to delete, to avoid + // infinite looping if we can't delete anything + if (segmentIdx === currentSegment && deletedStuff) { + this.reportDebug('Retrying appendSegment for', segmentIdx); + this.appendSegment(segmentIdx, chunk); + } else { + this.reportDebug('Not retrying segment', segmentIdx); + this.sidx.entries[segmentIdx].requested = false; } return; } @@ -440,7 +447,7 @@ Stream.prototype.fetchSegment = function(segmentIdx) { this.url, entry.start, entry.end, - this.appendSegment.bind(this, segmentIdx, this.avMerge.seeking), + this.appendSegment.bind(this, segmentIdx), ); } Stream.prototype.fetchSegmentIfNeeded = function(segmentIdx) { |