Skip to content
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
02342be
Feature: MPEG-5 LCEVC Scalable support
v-nova-romas Sep 13, 2024
b390b65
Update README.md
sortonjay Sep 17, 2024
34b02d8
Update README.md
sortonjay Sep 17, 2024
1397f4b
Merge branch 'development' into feature/AddLcevcScalableSupport
v-nova-romas Sep 23, 2024
3538ead
Merge remote-tracking branch 'origin/development' into feature/AddLce…
v-nova-romas Oct 1, 2024
7ba8123
Update tests after merging latest changes from development
v-nova-romas Oct 1, 2024
ef8e0dd
Merge branch 'development' into feature/AddLcevcScalableSupport
v-nova-romas Oct 9, 2024
e909c28
Merge branch 'development' into feature/AddLcevcScalableSupport
v-nova-romas Oct 15, 2024
ac687cc
Sort member attributes alphabetically
v-nova-romas Oct 15, 2024
4a3f963
Merge branch 'development' into feature/AddLcevcScalableSupport
v-nova-romas Nov 21, 2024
064329d
Merge branch 'development' into feature/AddLcevcScalableSupport
v-nova-romas Apr 14, 2025
d2c98d4
Update dash.js lib location in Sample pages
v-nova-romas Apr 14, 2025
17ce0fa
Merge branch 'Dash-Industry-Forum:development' into feature/AddLcevcS…
v-nova-romas Jul 30, 2025
49dc8bd
Set enhancement to CmcdObjectType.OTHER
v-nova-romas Aug 5, 2025
b800633
Make enhancement configurable via Settings.js
v-nova-romas Aug 6, 2025
91dd2b4
Update demo contents
v-nova-romas Aug 26, 2025
56b1000
Add Scalable Carriage demo to Reference Player
v-nova-romas Aug 28, 2025
b55db37
Place all samples into root folder lcevc
v-nova-romas Sep 2, 2025
66483c9
Rephrase wording in samples.json
v-nova-romas Sep 2, 2025
6d09eaa
Document EnhancementSettings module
v-nova-romas Sep 2, 2025
6cf68df
Rename to full name codec
v-nova-romas Sep 2, 2025
78ac06f
Check if ExernalSourceBuffer
v-nova-romas Sep 2, 2025
8c6b2fe
Sort setEnhancementStreamProcessor alphabetically
v-nova-romas Sep 2, 2025
3234d6f
Move enhancementStreamProcessor declaration
v-nova-romas Sep 3, 2025
9b4d53c
Rename aData, aStart, anEnd
v-nova-romas Sep 3, 2025
ae253f2
Move to private function _selectMediaInfoForEnhancementStreamProcessor
v-nova-romas Sep 5, 2025
4f610b4
Rename aType to streamProcessorMediaType
v-nova-romas Sep 5, 2025
099f909
Add new attribute dependencyId to save raw values
v-nova-romas Sep 9, 2025
254b36f
Delete unnecessary comment
v-nova-romas Sep 9, 2025
66ebf32
Provide reset method in ExternalMediaSource.js
v-nova-romas Sep 9, 2025
ad70430
Create _prepareQualityChangeForEnhancementStreamProcessor
v-nova-romas Sep 10, 2025
83f31f2
Remove limitation to video in Capabilities.js
v-nova-romas Sep 11, 2025
83d8166
Adjust index.d.ts definitions to add new functions and classes
v-nova-romas Sep 12, 2025
dcd493f
Adjust index.d.ts to include enhancement
v-nova-romas Sep 12, 2025
c655991
Add higher resolution exports and source files for diagrams
v-nova-romas Sep 12, 2025
b900f12
Remove getCurrentRepresentation overload
v-nova-romas Sep 24, 2025
1a5b1db
Remove getAbrRepresentation function
v-nova-romas Sep 24, 2025
64ec29b
Do not resolve dependency in _addRepresentationSwitch
v-nova-romas Sep 24, 2025
d46bc04
Remove documentation to be moved to gh-pages branch
v-nova-romas Sep 25, 2025
4622811
Do not include compatible MediaInfos
v-nova-romas Sep 25, 2025
f43608f
Update unit tests to pass
v-nova-romas Sep 25, 2025
12c2205
Accept whitespace-separated list of values for dependencyId
v-nova-romas Sep 25, 2025
fa10394
Make _getCurrentDependentRepresentation private
v-nova-romas Oct 14, 2025
2627ca9
Add comment for multiple dependency ids
v-nova-romas Oct 14, 2025
3e363e4
Make UI Checkbox more descriptive
v-nova-romas Oct 14, 2025
6f7423a
Trigger GitHub PR refresh
v-nova-romas Oct 14, 2025
868b4bf
Reuse reset function in ExternalMediaSource constructor
v-nova-romas Oct 15, 2025
d6a8865
Fix UI buttons in controlbar being shifted up
v-nova-romas Oct 15, 2025
68d2584
Merge remote-tracking branch 'upstream/development' into feature/AddL…
v-nova-romas Oct 16, 2025
8ef5c85
Cleanup after merge
v-nova-romas Oct 16, 2025
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
37 changes: 37 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,8 @@ declare namespace dashjs {
bitsPerPixel: number;
codecPrivateData: string | null;
codecs: string | null;
dependencyId: string | null;
dependentRepresentation: object | null;
fragmentDuration: number | null;
frameRate: number;
height: number;
Expand Down Expand Up @@ -1911,6 +1913,10 @@ declare namespace dashjs {
etpWeightRatio?: number
}
},
enhancement?: {
enabled?: boolean,
codecs?: Array<string>
},
defaultSchemeIdUri?: {
viewpoint?: string,
audioChannelConfiguration?: string,
Expand Down Expand Up @@ -2836,6 +2842,7 @@ declare namespace dashjs {
export interface Constants {
STREAM: 'stream',
VIDEO: 'video',
ENHANCEMENT: 'enhancement',
AUDIO: 'audio',
TEXT: 'text',
MUXED: 'muxed',
Expand Down Expand Up @@ -5874,12 +5881,42 @@ declare namespace dashjs {

selectMediaInfo(selectionInput: object): Promise<any>;

setEnhancementStreamProcessor(value: StreamProcessor): void;

setExplicitBufferingTime(value: number): void;

setMediaSource(mediaSource: MediaSource): void;

updateStreamInfo(newStreamInfo: StreamInfo): Promise<any>;
}

export interface ExternalMediaSource {
duration: number | null;

readyState: string;

addSourceBuffer(mimeType: string): ExternalSourceBuffer;

close(): void;

endOfStream(): void;

open(): void;

removeSourceBuffer(sourceBuffer: ExternalSourceBuffer): void;

reset(): void;
}

export interface ExternalSourceBuffer {
buffered: TimeRanges;

abort(): void;

appendBuffer(segmentData: ArrayBuffer, segmentStartTime: number, segmentEndTime: number): void;

remove(start: number, end: number): void;
}

export interface XlinkLoader {
load(url: string, element: any, resolveObject: object): void;
Expand Down
5 changes: 5 additions & 0 deletions samples/dash-if-reference-player/app/contributors.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
"name": "Broadpeak",
"logo": "app/img/broadpeak.png",
"link": "https://broadpeak.tv/"
},
{
"name": "V-Nova",
"logo": "app/img/v-nova.png",
"link": "https://v-nova.com/"
}
]
}
7 changes: 7 additions & 0 deletions samples/dash-if-reference-player/app/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ a:hover {

.dash-video-player {
background-color: #000000;
position: relative;
margin: 0 auto;
line-height: 1.0;
}

.col-md-9 video {
Expand All @@ -178,6 +181,10 @@ a:hover {
margin-top: -5px !important;
}

.element-hidden {
display: none !important;
}

.btn-play-pause,
.control-icon-layout {
padding: 4px 10px !important;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
140 changes: 139 additions & 1 deletion samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'

$scope.conformanceViolations = [];

$scope.enhancementDecoder = null;

var defaultExternalSettings = {
mpd: encodeURIComponent('https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd'),
loop: true,
Expand Down Expand Up @@ -1040,6 +1042,126 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
});
};

$scope.toggleEnhancementEnabled = function () {
const video = document.querySelector('video');
const canvas = document.querySelector('canvas');

if ($scope.enhancementEnabled) {
canvas.classList.remove('element-hidden');
video.classList.add('element-hidden');
} else {
canvas.classList.add('element-hidden');
video.classList.remove('element-hidden');
}
};

$scope.setupEnhancementDecoder = function () {
/**
* MPEG-5 LCEVC Integration for Dash.js Player.
*
* These are the changes needed for passing the correct
* data to lcevc_dec.js and trigger the correct methods
* at the correct time.
*/

/**
* Let the LCEVC Decoder Library make the decision as to when to switch, based on the currently
* rendered frame. If disabled, the player needs to signal LCEVC when there is a render change
* after an ABR switch happens.
*
* @readonly
* @enum {number}
* @public
*/
const AutoRenderMode = {
DISABLED: 0,
ENABLED: 1
};

dashjs.Extensions = {
...dashjs.Extensions,
/**
* Attaches LCEVC functionality and methods to the provided Dash.js player instance.
*
* @param {object} player the Dash.js player instance to attach LCEVC to
*/
useLcevc: function useLcevc(player) {
if (!player) {
throw new TypeError('The provided Dash.js player instance was null or undefined.');
}
const { LCEVCdec } = window;
if (!LCEVCdec) {
throw new TypeError('LCEVC Decoder Libraries could not be loaded.');
}

let abrIndex = -1;

player.attachLcevc = function attachLcevc(media, canvas, LCEVCdecConfig) {
player.LCEVCdec = new LCEVCdec.LCEVCdec(
media,
canvas,
LCEVCdecConfig
);

/* Signal profile information and switches to LCEVCdecJS */
player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, handleQualityChange);
player.on(dashjs.MediaPlayer.events.FRAGMENT_LOADING_COMPLETED, handleFragmentLoadingCompleted);
player.on(dashjs.MediaPlayer.events.REPRESENTATION_SWITCH, handleRepresentationSwitch);
player.on('externalSourceBufferUpdateStart', handleBufferUpdates);
};

function handleFragmentLoadingCompleted(event) {
if (event.mediaType === 'enhancement') {
abrIndex = event.request.representation.absoluteIndex;
}
}

function handleQualityChange(event) {
if (event.mediaType === 'video' || event.mediaType === 'enhancement') {
const index = event.newRepresentation.absoluteIndex;
console.log('>>> requested:', event.mediaType, index);
player.LCEVCdec.setLevelSwitching(index, AutoRenderMode.ENABLED);
}
}

function handleRepresentationSwitch(event) {
if (event.mediaType === 'video' || event.mediaType === 'enhancement') {
const rep = event.currentRepresentation;
const index = rep.absoluteIndex;
// Workaround for very first representation played for which no QUALITY_CHANGE_REQUESTED arrives
if (rep && rep.dependentRepresentation) {
console.log('>>> rep switch:', event.mediaType, index);
player.LCEVCdec.setLevelSwitching(index, AutoRenderMode.ENABLED);
}
}
}

function handleBufferUpdates(event) {
if (event.request === 'appendBuffer') {
player.LCEVCdec.appendBuffer(event.data, 'video', abrIndex, 0, /* isMuxed */ false);
}
else if (event.request === 'remove') {
player.LCEVCdec.flushBuffer(event.start, event.end);
}
}
}
};

const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const LCEVCdecConfig = {
dynamicPerformanceScaling: false
};

window.LCEVCdec.ready.then(() => {
/* Attach LCEVC to the Dash.js player instance */
const player = $scope.player;
dashjs.Extensions.useLcevc(player);
player.attachLcevc(video, canvas, LCEVCdecConfig);
$scope.enhancementDecoder = player.LCEVCdec;
});
};

$scope.toggleCmsdApplyMb = function () {
$scope.player.updateSettings({
streaming: {
Expand Down Expand Up @@ -1109,7 +1231,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
liveDelay: $scope.defaultLiveDelay
},
abr: {},
cmcd: {}
cmcd: {},
enhancement: {}
}
};

Expand Down Expand Up @@ -1175,6 +1298,21 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
config.streaming.cmcd.rtpSafetyFactor = $scope.cmcdRtpSafetyFactor ? $scope.cmcdRtpSafetyFactor : null;
config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope._getFormatedCmcdEnabledKeys() : [];

// Cleanup enhancement decoder if it exists from previous playback
if ($scope.enhancementDecoder) {
$scope.enhancementDecoder.close();
$scope.enhancementDecoder = null;
}

// Setup enhancement decoder if checkbox is checked or if stream is from V-Nova
if ($scope.enhancementEnabled || $scope.selectedItem.provider === 'v-nova') {
config.streaming.enhancement.enabled = true;
$scope.enhancementEnabled = true;
$scope.setupEnhancementDecoder();
}

$scope.toggleEnhancementEnabled();

$scope.player.updateSettings(config);

$scope.controlbar.reset();
Expand Down
20 changes: 20 additions & 0 deletions samples/dash-if-reference-player/app/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
"acronym": "ARTE",
"name": "ARTE",
"url": "https://www.arte.tv/en/"
},
"v-nova": {
"acronym": "V-Nova",
"name": "V-Nova",
"url": "https://v-nova.com/"
}
},
"items": [
Expand Down Expand Up @@ -1303,6 +1308,21 @@
"provider": "microsoft"
}
]
},
{
"name": "MPEG-5 Part 2 - LCEVC",
"submenu": [
{
"url": "https://s3.eu-west-1.amazonaws.com/origin-prod-lon-v-nova.com/lcevcDualTrack/1080p30_4Mbps_no_dR/master.mpd",
"name": "Scalable Carriage",
"provider": "v-nova"
},
{
"url": "https://s3.eu-west-1.amazonaws.com/origin-prod-lon-v-nova.com/lcevcDualTrack/1080p30_4Mbps_with_dR/master.mpd",
"name": "Scalable with Debug Residuals",
"provider": "v-nova"
}
]
}
]
}
16 changes: 16 additions & 0 deletions samples/dash-if-reference-player/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
<script src="app/main.js"></script>
<script src="app/rules/ThroughputRule.js"></script>

<!-- LCEVC Decoder -->
<script src="https://unpkg.com/lcevc_dec.js@latest/dist/lcevc_dec.min.js"></script>

<!-- Google Cast -->
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>

Expand Down Expand Up @@ -922,6 +925,17 @@
ng-change="updateCmsdEtpWeightRatio()">
</div>
</div>
<div class="options-item">
<div class="options-item-title">Enhancement</div>
<div class="options-item-body">
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Enables enhancement. The provided stream should contain two adaptation sets, one containing the base layer and the second containing an enhancement layer. Checkbox selection will be applied the next time a stream is loaded.">
<input type="checkbox" ng-model="enhancementEnabled" ng-change="toggleEnhancementEnabled()"
ng-checked="enhancementEnabled">
Enable Enhancement
</label>
</div>
</div>

</div>

Expand All @@ -930,6 +944,8 @@
<div class="dash-video-player col-md-9">
<div id="videoContainer" class="videoContainer">
<video disableRemotePlayback="true"></video>
<canvas id="enhacementCanvas" class="enhancementCanvas element-hidden"></canvas>
<div style="min-height: 1px;"></div>
<div id="video-caption"></div>
<div id="cast-msg" ng-if="isCasting">
{{ castPlayerState === 'IDLE' ? 'Ready to cast stream' : castPlayerState }}
Expand Down
Loading