UNPKG

shaka-player

Version:
186 lines (169 loc) • 5.08 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.msf.JoinGate'); goog.require('shaka.log'); goog.require('shaka.util.Timer'); goog.requireType('shaka.msf.Utils'); /** * Puts a subscription that joins the current Group back in Location order. * * Joining delivers the current Group twice over: its first Objects on a * fetch stream (a Joining FETCH in draft-18, a fill fetch stream in draft-20 * and later), and the rest of it, mid-group, on the subscription's own * streams. The two race, so the subscription's Objects can arrive first. * A segmenter or a segment index that is fed in arrival order then sees the * end of a Group before its start. * * So the fill is passed through as it comes, since it is the earliest part, * and the subscription's Objects are held until the fill is over. Then they * are handed on in Location order, and from there on the subscription passes * straight through. Whatever the subscription repeats of the fill is dropped. * * The fill is over when it reaches the Largest Object the SUBSCRIBE_OK * reported, when its stream ends or fails, or, for a publisher that never * sends it, after a timeout. A fill Object that arrives after that is too late * to be put in order and is dropped. * * @final */ shaka.msf.JoinGate = class { /** * @param {shaka.msf.Utils.ObjectCallback} callback * @param {string} description Used in log messages. */ constructor(callback, description) { /** @private {shaka.msf.Utils.ObjectCallback} */ this.callback_ = callback; /** @private {string} */ this.description_ = description; /** * The subscription's Objects, while the fill is still running; null once * it is over. * @private {?Array<shaka.msf.Utils.MOQObject>} */ this.held_ = []; /** * The last Object the fill delivered. * @private {?shaka.msf.Utils.Location} */ this.fillEnd_ = null; /** * Where the fill ends, once the SUBSCRIBE_OK has said. * @private {?shaka.msf.Utils.Location} */ this.largestObject_ = null; /** @private {shaka.util.Timer} */ this.timer_ = new shaka.util.Timer(() => { shaka.log.warning( `${this.description_}: the current Group did not arrive in time; ` + 'playing from the subscription alone'); this.open(); }); } /** * Hands on an Object of the fill. * * @param {shaka.msf.Utils.MOQObject} obj */ fromFill(obj) { if (!this.held_) { return; } this.fillEnd_ = obj.location; this.callback_(obj); if (this.largestObject_ && shaka.msf.JoinGate.compare_(obj.location, this.largestObject_) >= 0) { this.open(); } } /** * Hands on an Object of the subscription, or holds it until the fill is * over. * * @param {shaka.msf.Utils.MOQObject} obj */ fromSubscription(obj) { if (this.held_) { this.held_.push(obj); } else if (!this.isFilled_(obj)) { this.callback_(obj); } } /** * Says what the SUBSCRIBE_OK reported as the Largest Object, which is where * the fill ends, and starts waiting for it. * * @param {(?shaka.msf.Utils.Location|undefined)} largestObject Null when * nothing has been published on the track, so there is nothing to fill; * undefined when the SUBSCRIBE_OK could not tell, so the fill ends only * when its stream does. * @param {number} timeoutSeconds How long to wait for the fill. */ expect(largestObject, timeoutSeconds) { if (largestObject === null) { this.open(); return; } this.largestObject_ = largestObject || null; if (largestObject && this.fillEnd_ && shaka.msf.JoinGate.compare_(this.fillEnd_, largestObject) >= 0) { this.open(); return; } if (this.held_) { this.timer_.tickAfter(timeoutSeconds); } } /** * Ends the fill and hands on what the subscription delivered meanwhile. */ open() { this.timer_.stop(); const held = this.held_; if (!held) { return; } this.held_ = null; held.sort((a, b) => shaka.msf.JoinGate.compare_(a.location, b.location)); for (const obj of held) { if (!this.isFilled_(obj)) { this.callback_(obj); } } } /** * Stops handing anything on. */ release() { this.timer_.stop(); this.held_ = null; this.callback_ = () => {}; } /** * @param {shaka.msf.Utils.MOQObject} obj * @return {boolean} Whether the fill already delivered this Object. * @private */ isFilled_(obj) { return !!this.fillEnd_ && shaka.msf.JoinGate.compare_(obj.location, this.fillEnd_) <= 0; } /** * @param {shaka.msf.Utils.Location} a * @param {shaka.msf.Utils.Location} b * @return {number} * @private */ static compare_(a, b) { if (a.group != b.group) { return a.group < b.group ? -1 : 1; } if (a.object != b.object) { return a.object < b.object ? -1 : 1; } return 0; } };