mirror of
https://github.com/iv-org/invidious.git
synced 2026-07-27 16:06:47 -05:00
Segment index parsers (mp4/webm) declared the per-segment URI array with
`var` inside the loop, so every SegmentReference's getUris() closure
captured the same function-scoped binding and returned the LAST segment's
URL. YouTube received the final segment's startTimeMs/sq for every
segment, replied with policy-only UMP (no media), and the player looped
forever on a black screen ("SABR throttled by YouTube"). Use `let`
(block-scoped, fresh per iteration) to match FreeTube's `const`.
Also scope the big centered play button styling to
`.shaka-play-button-container`; the bare `.shaka-play-button` selector
also matched the control-bar play button (48px siblings), oversizing it
to ~54px and pushing it out of alignment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
112 lines
3.6 KiB
JavaScript
112 lines
3.6 KiB
JavaScript
// Port of FreeTube's Mp4SegmentIndexParser.js
|
|
// Based on shaka-player's dash/mp4_segment_index_parser.js
|
|
// Reads shaka from window.shaka (loaded via <script>).
|
|
|
|
'use strict';
|
|
|
|
(function () {
|
|
var shaka = window.shaka;
|
|
var ShakaError = shaka.util.Error;
|
|
var SeverityCritical = ShakaError.Severity.CRITICAL;
|
|
var CategoryMedia = ShakaError.Category.MEDIA;
|
|
|
|
function parseSIDX(sidxOffset, initSegmentReference, timestampOffset, appendWindowStart, appendWindowEnd, uri, box) {
|
|
var references = [];
|
|
|
|
box.reader.skip(4);
|
|
|
|
var timescale = box.reader.readUint32();
|
|
if (timescale === 0) {
|
|
console.error('[parseMp4SegmentIndex] Invalid timescale.');
|
|
throw new ShakaError(SeverityCritical, CategoryMedia, ShakaError.Code.MP4_SIDX_INVALID_TIMESCALE);
|
|
}
|
|
|
|
var earliestPresentationTime;
|
|
var firstOffset;
|
|
if (box.version === 0) {
|
|
earliestPresentationTime = box.reader.readUint32();
|
|
firstOffset = box.reader.readUint32();
|
|
} else {
|
|
earliestPresentationTime = box.reader.readUint64();
|
|
firstOffset = box.reader.readUint64();
|
|
}
|
|
|
|
box.reader.skip(2);
|
|
var referenceCount = box.reader.readUint16();
|
|
|
|
var unscaledStartTime = earliestPresentationTime;
|
|
var startByte = sidxOffset + box.size + firstOffset;
|
|
|
|
for (var i = 0; i < referenceCount; i++) {
|
|
var chunk = box.reader.readUint32();
|
|
var referenceType = (chunk & 0x80000000) >>> 31;
|
|
var referenceSize = chunk & 0x7FFFFFFF;
|
|
|
|
var subsegmentDuration = box.reader.readUint32();
|
|
box.reader.skip(4);
|
|
|
|
if (referenceType === 1) {
|
|
console.error('[parseMp4SegmentIndex] Hierarchical SIDXs are not supported.');
|
|
throw new ShakaError(SeverityCritical, CategoryMedia, ShakaError.Code.MP4_SIDX_TYPE_NOT_SUPPORTED);
|
|
}
|
|
|
|
var nativeStartTime = unscaledStartTime / timescale;
|
|
var nativeEndTime = (unscaledStartTime + subsegmentDuration) / timescale;
|
|
|
|
// NOTE: must be `let` (block-scoped). With `var` the closure below
|
|
// captures the function-scoped binding, so every SegmentReference would
|
|
// return the LAST segment's URL (wrong startTimeMs/sq -> SABR serves no media).
|
|
let uris = [uri + '&startTimeMs=' + Math.round((nativeStartTime + timestampOffset) * 1000) + '&sq=' + (i + 1)];
|
|
|
|
references.push(
|
|
new shaka.media.SegmentReference(
|
|
nativeStartTime + timestampOffset,
|
|
nativeEndTime + timestampOffset,
|
|
function () { return uris; },
|
|
startByte,
|
|
startByte + referenceSize - 1,
|
|
initSegmentReference,
|
|
timestampOffset,
|
|
appendWindowStart,
|
|
appendWindowEnd
|
|
)
|
|
);
|
|
|
|
unscaledStartTime += subsegmentDuration;
|
|
startByte += referenceSize;
|
|
}
|
|
|
|
box.parser.stop();
|
|
return references;
|
|
}
|
|
|
|
function parseMp4SegmentIndex(sidxData, sidxOffset, uri, initSegmentReference, timestampOffset, appendWindowStart, appendWindowEnd) {
|
|
var references;
|
|
|
|
var parser = new shaka.util.Mp4Parser()
|
|
.fullBox('sidx', function (box) {
|
|
references = parseSIDX(
|
|
sidxOffset,
|
|
initSegmentReference,
|
|
timestampOffset,
|
|
appendWindowStart,
|
|
appendWindowEnd,
|
|
uri,
|
|
box
|
|
);
|
|
});
|
|
|
|
if (sidxData) {
|
|
parser.parse(sidxData);
|
|
}
|
|
|
|
if (references) {
|
|
return references;
|
|
} else {
|
|
console.error('[parseMp4SegmentIndex] Invalid box type, expected "sidx".');
|
|
throw new ShakaError(SeverityCritical, CategoryMedia, ShakaError.Code.MP4_SIDX_WRONG_BOX_TYPE);
|
|
}
|
|
}
|
|
|
|
window.parseMp4SegmentIndex = parseMp4SegmentIndex;
|
|
})(); |