/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.msf.packaging.Loc');
goog.require('shaka.log');
goog.require('shaka.media.InitSegmentReference');
goog.require('shaka.media.SegmentUtils');
goog.require('shaka.msf.LOCParser');
goog.require('shaka.msf.PackagingRegistry');
/**
* Packaging for Low Overhead Container tracks, where every MoQT object is one
* frame of a raw elementary bitstream.
*
* A frame carries no container, so nothing about the media can be read out of
* the objects themselves: the codec comes from the catalog, and timing is a
* fixed frame duration derived from the catalog's framerate or samplerate,
* with the frame's own timestamp used only to place it.
*
* @see https://www.ietf.org/archive/id/draft-ietf-moq-loc-02.html
*
* @implements {shaka.extern.MsfPackaging}
* @final
*/
shaka.msf.packaging.Loc = class {
constructor() {
/** @private {number} */
this.frameDuration_ = 0;
}
/**
* @override
*/
describeTrack(track, initData) {
if (!track.codec) {
shaka.log.info('Skipping LOC track with no codec', track);
return null;
}
// For LOC tracks compute the fixed frame duration from MSF catalog fields
// so that timing never depends on the optional LOC Timestamp property.
const frameDuration = shaka.msf.LOCParser.frameDurationFromTrack(track);
if (frameDuration === null) {
shaka.log.warning(`LOC track "${track.name}" has no usable frame ` +
'duration; skipping.');
return null;
}
this.frameDuration_ = frameDuration;
const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMimeType(
`moq/loc; codecs="${track.codec}"`);
// LOC has no initialization segment. A track that declares a timescale
// still gets a reference for it, because downstream consumers read the
// timescale off the initialization segment reference.
let initSegmentReference = null;
if (track.timescale) {
initSegmentReference = new shaka.media.InitSegmentReference(
() => [],
/* startBytes= */ 0,
/* endBytes= */ null,
/* mediaQuality= */ null,
track.timescale,
initData);
}
return {basicInfo, initSegmentReference};
}
/**
* @override
*/
createSegmenter() {
return new shaka.msf.packaging.LocSegmenter(
new shaka.msf.LOCParser(this.frameDuration_));
}
};
/**
* @implements {shaka.extern.MsfSegmenter}
* @final
*/
shaka.msf.packaging.LocSegmenter = class {
/**
* @param {!shaka.msf.LOCParser} parser
*/
constructor(parser) {
/** @private {!shaka.msf.LOCParser} */
this.parser_ = parser;
}
/**
* @override
*/
push(obj) {
if (!obj.data.byteLength) {
return [];
}
const result = this.parser_.parse(obj);
// The payload is empty once the private properties prefix is stripped when
// the object carried properties but no bitstream.
if (!result.duration || !result.payload.byteLength) {
return [];
}
return [{
startTime: result.startTime,
duration: result.duration,
data: result.payload,
timestampOffset: 0,
discontinuitySequence: -1,
}];
}
};
shaka.msf.PackagingRegistry.registerPackaging(
'loc', () => new shaka.msf.packaging.Loc());