Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,24 @@ For details on the HLS format and these tags' meanings, see https://datatracker.

</details>

## MOQT Streaming Format (MSF) (Experimental)

Features supported:
- Media over QUIC Transport [draft-11](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/11/)
- MOQT Streaming Format [draft-01](https://datatracker.ietf.org/doc/draft-ietf-moq-warp/01/)
- Audio and Video
- ABR (only navigator.connection change event)
- Encrypted content with PSSH in the initData
- MP4 / CMAF support
- Live
- For browsers that support WebTransport certificate fingerprints (e.g., Chrome), you can use self-signed certificates without installing them.

Features **not** supported:
- VOD
- MOQT Streaming Format catalog updates

Note: This module is experimental and is only included in the experimental build.


## MPEG-5 Part2 LCEVC Support

Expand Down Expand Up @@ -384,6 +402,7 @@ NOTES:
## Builds

Shaka currently provides the following versions:
- Complete build with UI + Experimental features (`shaka-player.experimental.js`)
- Complete build with UI (`shaka-player.ui.js`)
- Complete build without UI (`shaka-player.compiled.js`)
- DASH build without UI, Cast and Offline (`shaka-player.dash.js`)
Expand Down
14 changes: 10 additions & 4 deletions build/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,23 +114,28 @@ def main(args):
if not compile_less('demo', 'demo', parsed_args):
return 1

build_args_with_ui = ['--name', 'ui', '+@complete']
complete_non_experimental = ['+@complete', '-@msf']

build_args_experimental = ['--name', 'experimental', '+@complete']
build_args_experimental += ['--locales'] + parsed_args.locales
build_args_with_ui = ['--name', 'ui', *complete_non_experimental]
build_args_with_ui += ['--locales'] + parsed_args.locales
build_args_without_ui = [
'--name', 'compiled', '+@complete', '-@ui', '-@polyfillForUI',
'--name', 'compiled', *complete_non_experimental, '-@ui', '-@polyfillForUI',
]
build_args_only_dash_without_ui = [
'--name', 'dash',
'+@complete', '-@ui', '-@polyfillForUI', '-@queue',
*complete_non_experimental, '-@ui', '-@polyfillForUI', '-@queue',
'-@hls', '-@transmuxer', '-@offline', '-@cast', '-@optionalText', '-@ads',
]
build_args_only_hls_without_ui = [
'--name', 'hls',
'+@complete', '-@ui', '-@polyfillForUI', '-@queue',
*complete_non_experimental, '-@ui', '-@polyfillForUI', '-@queue',
'-@dash', '-@offline', '-@cast', '-@optionalText', '-@ads',
]

if parsed_args.force:
build_args_experimental += ['--force']
build_args_with_ui += ['--force']
build_args_without_ui += ['--force']
build_args_only_dash_without_ui += ['--force']
Expand All @@ -147,6 +152,7 @@ def main(args):
modes += ['debug', 'release']

builds = [
build_args_experimental,
build_args_with_ui,
build_args_without_ui,
build_args_only_dash_without_ui,
Expand Down
1 change: 1 addition & 0 deletions build/conformance.textproto
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ requirement: {
whitelist_regexp: "lib/util/buffer_utils.js"
whitelist_regexp: "lib/util/object_utils.js"
whitelist_regexp: "test/util/buffer_utils_unit.js"
whitelist_regexp: "lib/msf/msf_classes.js"
}

# Disallow Element.innerHTML and outerHTML.
Expand Down
1 change: 1 addition & 0 deletions build/types/manifests
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

+@dash
+@hls
+@msf
+@offline
7 changes: 7 additions & 0 deletions build/types/msf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The MOQT Streaming Format manifest parser plugin.

+../../lib/msf/buffer_control_writer.js
+../../lib/msf/msf_classes.js
+../../lib/msf/msf_parser.js
+../../lib/msf/msf_transport.js
+../../lib/msf/msf_utils.js
15 changes: 15 additions & 0 deletions demo/common/assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ shakaAssets.Source = {
JWPLAYER: 'JW Player',
BBC: 'BBC',
DOLBY: 'Dolby',
EYEVINN: 'Eyevinn',
};


Expand Down Expand Up @@ -162,6 +163,8 @@ shakaAssets.Feature = {
DASH: 'DASH',
// Set if the asset is an HLS manifest.
HLS: 'HLS',
// Set if the asset is an MSF.
MSF: 'MSF',

// Set if the asset has at least one image stream.
THUMBNAILS: 'Thumbnails',
Expand Down Expand Up @@ -2353,5 +2356,17 @@ shakaAssets.testAssets = [
.addFeature(shakaAssets.Feature.HIGH_DEFINITION)
.addFeature(shakaAssets.Feature.OFFLINE),
// }}}

// Eyevinn assets {{{
/* Eyevinn Contents */
new ShakaDemoAssetInfo(
/* name= */ 'moqlivemock',
/* iconUri= */ '',
/* manifestUri= */ 'https://moqlivemock.demo.osaas.io/moq',
/* source= */ shakaAssets.Source.EYEVINN)
.addFeature(shakaAssets.Feature.MSF)
.addFeature(shakaAssets.Feature.MP4)
.setMimeType('application/msf'),
// }}}
];
/* eslint-enable @stylistic/max-len */
9 changes: 9 additions & 0 deletions demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ shakaDemo.Config = class {
this.addManifestSection_();
this.addDashManifestSection_();
this.addHlsManifestSection_();
this.addMsfManifestSection_();
this.addRetrySection_('manifest', 'Manifest Retry Parameters');
this.addRestrictionsSection_('', 'Restrictions');
this.addTextDisplayerSection_();
Expand Down Expand Up @@ -296,6 +297,14 @@ shakaDemo.Config = class {
'manifest.hls.allowRangeRequestsToGuessMimeType');
}

/** @private */
addMsfManifestSection_() {
const docLink = this.resolveExternLink_('.ManifestConfiguration');
this.addSection_('MSF', docLink)
.addTextInput_('Fingerprint URI',
'manifest.msf.fingerprintUri');
}

/** @private */
addAbrSection_() {
const docLink = this.resolveExternLink_('.AbrConfiguration');
Expand Down
2 changes: 1 addition & 1 deletion demo/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ shakaDemo.Search = class {
container, /* headerText= */ null, shakaDemo.InputContainer.Style.FLEX,
/* docLink= */ null);
this.makeSelectInput_(coreContainer, 'Manifest',
[Feature.DASH, Feature.HLS], FEATURE);
[Feature.DASH, Feature.HLS, Feature.MSF], FEATURE);
this.makeSelectInput_(coreContainer, 'Container',
[Feature.MP4, Feature.MP2TS, Feature.WEBM, Feature.CONTAINERLESS],
FEATURE);
Expand Down
17 changes: 17 additions & 0 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,20 @@ shaka.extern.DashManifestConfiguration;
*/
shaka.extern.HlsManifestConfiguration;

/**
* @typedef {{
* fingerprintUri: string,
* }}
*
* @property {string} fingerprintUri
* A fingerprint URI. If set, the server fingerprint will be fetched from
* this URL. This is required to use self-signed certificates with Chromium.
* <br>
* Defaults to <code>''</code>.
* @exportDoc
*/
shaka.extern.MsfManifestConfiguration;


/**
* @typedef {{
Expand All @@ -1634,6 +1648,7 @@ shaka.extern.HlsManifestConfiguration;
* segmentRelativeVttTiming: boolean,
* dash: shaka.extern.DashManifestConfiguration,
* hls: shaka.extern.HlsManifestConfiguration,
* msf: shaka.extern.MsfManifestConfiguration,
* raiseFatalErrorOnManifestUpdateRequestFailure: boolean,
* continueLoadingWhenPaused: boolean,
* ignoreSupplementalCodecs: boolean,
Expand Down Expand Up @@ -1694,6 +1709,8 @@ shaka.extern.HlsManifestConfiguration;
* Advanced parameters used by the DASH manifest parser.
* @property {shaka.extern.HlsManifestConfiguration} hls
* Advanced parameters used by the HLS manifest parser.
* @property {shaka.extern.MsfManifestConfiguration} msf
* Advanced parameters used by the MSF.
* @property {boolean} raiseFatalErrorOnManifestUpdateRequestFailure
* If true, manifest update request failures will cause a fatal error.
* <br>
Expand Down
6 changes: 6 additions & 0 deletions lib/media/manifest_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ shaka.media.ManifestParser.HLS = 'HLS';
shaka.media.ManifestParser.DASH = 'DASH';


/**
* @const {string}
*/
shaka.media.ManifestParser.MSF = 'MSF';


/**
* @const {string}
*/
Expand Down
53 changes: 53 additions & 0 deletions lib/media/segment_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,59 @@ shaka.media.SegmentUtils = class {
return finalCodecs;
}

/**
* @param {!Uint8Array} data
* @param {number} timescale
* @return {{startTime: number, duration: number}}
*/
static getStartTimeAndDurationFromMp4(data, timescale) {
let startTime = 0;
let defaultSampleDuration = 0;
let sampleData = [];
const Mp4Parser = shaka.util.Mp4Parser;
new Mp4Parser()
.box('moof', Mp4Parser.children)
.box('traf', Mp4Parser.children)
.fullBox('tfhd', (box) => {
goog.asserts.assert(
box.flags != null,
'A TFHD box should have a valid flags value');
const parsedTFHDBox = shaka.util.Mp4BoxParsers.parseTFHD(
box.reader, box.flags);
defaultSampleDuration = parsedTFHDBox.defaultSampleDuration;
})
.fullBox('tfdt', (box) => {
goog.asserts.assert(
box.version == 0 || box.version == 1,
'TFDT version can only be 0 or 1');
const parsed = shaka.util.Mp4BoxParsers.parseTFDTInaccurate(
box.reader, box.version);
startTime = parsed.baseMediaDecodeTime / timescale;
})
.fullBox('trun', (box) => {
goog.asserts.assert(
box.version != null,
'A TRUN box should have a valid version value');
goog.asserts.assert(
box.flags != null,
'A TRUN box should have a valid flags value');
const parsedTRUNBox = shaka.util.Mp4BoxParsers.parseTRUN(
box.reader, box.version, box.flags);

sampleData = parsedTRUNBox.sampleData;
box.parser.stop();
}).parse(data);
let sumSampleDuration = 0;
for (const sample of sampleData) {
sumSampleDuration += sample.sampleDuration || defaultSampleDuration;
}
const duration = sumSampleDuration / timescale;
return {
startTime,
duration,
};
}

/**
* @param {!Array<string>} codecs
* @return {!Array<string>} codecs
Expand Down
Loading
Loading