@newrelic/video-videojs 4.0.2 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -1
- package/README.md +287 -67
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.LICENSE.txt +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.LICENSE.txt +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/newrelic-video-videojs.min.js +1 -1
- package/dist/umd/newrelic-video-videojs.min.js.LICENSE.txt +1 -1
- package/dist/umd/newrelic-video-videojs.min.js.map +1 -1
- package/package.json +5 -5
- package/src/techs/contrib-hls.js +74 -24
- package/src/techs/hls-js.js +69 -26
- package/src/techs/shaka.js +75 -23
- package/src/tracker.js +76 -151
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/video-videojs",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "New relic tracker for Videojs",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -30,16 +30,13 @@
|
|
|
30
30
|
"@babel/core": "^7.24.5",
|
|
31
31
|
"@babel/plugin-transform-modules-commonjs": "^7.24.1",
|
|
32
32
|
"@babel/preset-env": "^7.24.5",
|
|
33
|
-
"aws-sdk": "^2.920.0",
|
|
34
33
|
"@newrelic/newrelic-oss-cli": "^0.1.2",
|
|
34
|
+
"aws-sdk": "^2.920.0",
|
|
35
35
|
"babel-loader": "^9.1.3",
|
|
36
36
|
"videojs-ima": "2.1.0",
|
|
37
37
|
"webpack": "^5.91.0",
|
|
38
38
|
"webpack-cli": "^4.9.2"
|
|
39
39
|
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@newrelic/video-core": "^4.0.0"
|
|
42
|
-
},
|
|
43
40
|
"files": [
|
|
44
41
|
"THIRD_PARTY_NOTICES.md",
|
|
45
42
|
"dist",
|
|
@@ -50,5 +47,8 @@
|
|
|
50
47
|
],
|
|
51
48
|
"publishConfig": {
|
|
52
49
|
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@newrelic/video-core": "^4.0.0"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/techs/contrib-hls.js
CHANGED
|
@@ -1,45 +1,95 @@
|
|
|
1
1
|
export default class ContribHlsTech {
|
|
2
|
-
constructor
|
|
3
|
-
this.tech = tech.vhs
|
|
2
|
+
constructor(tech) {
|
|
3
|
+
this.tech = tech.vhs;
|
|
4
|
+
this.player = tech.el().player; // Store player reference for playback bitrate calculation
|
|
4
5
|
}
|
|
5
6
|
|
|
6
|
-
getRenditionName
|
|
7
|
+
getRenditionName() {
|
|
7
8
|
try {
|
|
8
|
-
var media = this.tech.playlists.media()
|
|
9
|
-
if (media && media.attributes) return media.attributes.NAME
|
|
10
|
-
} catch (err) {
|
|
11
|
-
return null
|
|
9
|
+
var media = this.tech.playlists.media();
|
|
10
|
+
if (media && media.attributes) return media.attributes.NAME;
|
|
11
|
+
} catch (err) {}
|
|
12
|
+
return null;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
getRenditionBitrate
|
|
15
|
+
getRenditionBitrate() {
|
|
15
16
|
try {
|
|
16
|
-
var media = this.tech.playlists.media()
|
|
17
|
-
if (media && media.attributes) return media.attributes.BANDWIDTH
|
|
18
|
-
} catch (err) {
|
|
19
|
-
return null
|
|
17
|
+
var media = this.tech.playlists.media();
|
|
18
|
+
if (media && media.attributes) return media.attributes.BANDWIDTH;
|
|
19
|
+
} catch (err) {}
|
|
20
|
+
return null;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
getRenditionWidth
|
|
23
|
+
getRenditionWidth() {
|
|
23
24
|
try {
|
|
24
|
-
var media = this.tech.playlists.media()
|
|
25
|
+
var media = this.tech.playlists.media();
|
|
25
26
|
if (media && media.attributes && media.attributes.RESOLUTION) {
|
|
26
|
-
return media.attributes.RESOLUTION.width
|
|
27
|
+
return media.attributes.RESOLUTION.width;
|
|
27
28
|
}
|
|
28
|
-
} catch (err) {
|
|
29
|
-
return null
|
|
29
|
+
} catch (err) {}
|
|
30
|
+
return null;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
getRenditionHeight
|
|
33
|
+
getRenditionHeight() {
|
|
33
34
|
try {
|
|
34
|
-
var media = this.tech.playlists.media()
|
|
35
|
+
var media = this.tech.playlists.media();
|
|
35
36
|
if (media && media.attributes && media.attributes.RESOLUTION) {
|
|
36
|
-
return media.attributes.RESOLUTION.height
|
|
37
|
+
return media.attributes.RESOLUTION.height;
|
|
37
38
|
}
|
|
38
|
-
} catch (err) {
|
|
39
|
-
return null
|
|
39
|
+
} catch (err) {}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getBitrate() {
|
|
44
|
+
// Calculate playback bitrate (actual content consumption rate)
|
|
45
|
+
return this.getContentBitratePlayback();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getManifestBitrate() {
|
|
49
|
+
try {
|
|
50
|
+
// Return highest available bitrate from all renditions
|
|
51
|
+
const playlists = this.tech.playlists.master.playlists;
|
|
52
|
+
if (playlists && playlists.length > 0) {
|
|
53
|
+
return Math.max(...playlists.map((p) => p.attributes.BANDWIDTH));
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getSegmentDownloadBitrate() {
|
|
60
|
+
if (
|
|
61
|
+
this.tech.stats?.bandwidth !== undefined &&
|
|
62
|
+
this.tech.stats.bandwidth > 0
|
|
63
|
+
) {
|
|
64
|
+
return this.tech.stats.bandwidth;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getNetworkDownloadBitrate() {
|
|
70
|
+
if (this.tech.throughput !== undefined && this.tech.throughput > 0) {
|
|
71
|
+
return this.tech.throughput;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getContentBitratePlayback() {
|
|
77
|
+
try {
|
|
78
|
+
// Get the current active rendition's bitrate from manifest
|
|
79
|
+
const media = this.tech.playlists.media();
|
|
80
|
+
if (media && media.attributes) {
|
|
81
|
+
// Use AVERAGE-BANDWIDTH if available, fallback to BANDWIDTH
|
|
82
|
+
return (
|
|
83
|
+
media.attributes['AVERAGE-BANDWIDTH'] ||
|
|
84
|
+
media.attributes.BANDWIDTH ||
|
|
85
|
+
null
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {}
|
|
89
|
+
return null;
|
|
40
90
|
}
|
|
41
91
|
}
|
|
42
92
|
|
|
43
93
|
ContribHlsTech.isUsing = function (tech) {
|
|
44
|
-
return !!tech.vhs
|
|
45
|
-
}
|
|
94
|
+
return !!tech.vhs;
|
|
95
|
+
};
|
package/src/techs/hls-js.js
CHANGED
|
@@ -1,45 +1,88 @@
|
|
|
1
1
|
export default class HlsJs {
|
|
2
|
-
constructor
|
|
3
|
-
this.tech = tech.vhs_
|
|
2
|
+
constructor(tech) {
|
|
3
|
+
this.tech = tech.vhs_;
|
|
4
|
+
this.player = tech.el().player; // Store player reference for currentTime
|
|
4
5
|
}
|
|
5
6
|
|
|
6
|
-
getResource
|
|
7
|
-
return this.tech.url
|
|
7
|
+
getResource(tech) {
|
|
8
|
+
return this.tech.url;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
getRenditionName
|
|
11
|
+
getRenditionName(tech) {
|
|
11
12
|
try {
|
|
12
|
-
var level = this.tech.levels[this.tech.currentLevel]
|
|
13
|
-
if (level && level.name) return level.name
|
|
14
|
-
} catch (err) {
|
|
15
|
-
return null
|
|
13
|
+
var level = this.tech.levels[this.tech.currentLevel];
|
|
14
|
+
if (level && level.name) return level.name;
|
|
15
|
+
} catch (err) {}
|
|
16
|
+
return null;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
getRenditionBitrate
|
|
19
|
+
getRenditionBitrate(tech) {
|
|
19
20
|
try {
|
|
20
|
-
var level = this.tech.levels[this.tech.currentLevel]
|
|
21
|
-
if (level && level.bitrate) return level.bitrate
|
|
22
|
-
} catch (err) {
|
|
23
|
-
return null
|
|
21
|
+
var level = this.tech.levels[this.tech.currentLevel];
|
|
22
|
+
if (level && level.bitrate) return level.bitrate;
|
|
23
|
+
} catch (err) {}
|
|
24
|
+
return null;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
getRenditionWidth
|
|
27
|
+
getRenditionWidth(tech) {
|
|
27
28
|
try {
|
|
28
|
-
var level = this.tech.levels[this.tech.currentLevel]
|
|
29
|
-
if (level && level.width) return level.width
|
|
30
|
-
} catch (err) {
|
|
31
|
-
return null
|
|
29
|
+
var level = this.tech.levels[this.tech.currentLevel];
|
|
30
|
+
if (level && level.width) return level.width;
|
|
31
|
+
} catch (err) {}
|
|
32
|
+
return null;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
getRenditionHeight
|
|
35
|
+
getRenditionHeight(tech) {
|
|
35
36
|
try {
|
|
36
|
-
var level = this.tech.levels[this.tech.currentLevel]
|
|
37
|
-
if (level && level.height) return level.height
|
|
38
|
-
} catch (err) {
|
|
39
|
-
return null
|
|
37
|
+
var level = this.tech.levels[this.tech.currentLevel];
|
|
38
|
+
if (level && level.height) return level.height;
|
|
39
|
+
} catch (err) {}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getBitrate() {
|
|
44
|
+
// Default uses playback bitrate (Method 4)
|
|
45
|
+
return this.getContentBitratePlayback();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getContentBitratePlayback() {
|
|
49
|
+
try {
|
|
50
|
+
// Get the current active level's bitrate from manifest
|
|
51
|
+
const level = this.tech.levels[this.tech.currentLevel];
|
|
52
|
+
if (level && level.bitrate) {
|
|
53
|
+
return level.bitrate;
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getManifestBitrate() {
|
|
60
|
+
try {
|
|
61
|
+
// Return highest available bitrate from all renditions
|
|
62
|
+
if (this.tech.levels && this.tech.levels.length > 0) {
|
|
63
|
+
return Math.max(...this.tech.levels.map(l => l.bitrate));
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getSegmentDownloadBitrate() {
|
|
70
|
+
try {
|
|
71
|
+
// VHS stats.bandwidth
|
|
72
|
+
if (this.tech.stats && this.tech.stats.bandwidth > 0)
|
|
73
|
+
return this.tech.stats.bandwidth;
|
|
74
|
+
} catch (err) {}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getNetworkDownloadBitrate() {
|
|
79
|
+
if (this.tech.throughput && this.tech.throughput > 0) {
|
|
80
|
+
return this.tech.throughput;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
40
83
|
}
|
|
41
84
|
}
|
|
42
85
|
|
|
43
86
|
HlsJs.isUsing = function (tech) {
|
|
44
|
-
return !!tech.vhs_
|
|
45
|
-
}
|
|
87
|
+
return !!tech.vhs_;
|
|
88
|
+
};
|
package/src/techs/shaka.js
CHANGED
|
@@ -1,49 +1,101 @@
|
|
|
1
1
|
export default class ShakaTech {
|
|
2
|
-
constructor
|
|
3
|
-
this.tech = tech.shakaPlayer
|
|
2
|
+
constructor(tech) {
|
|
3
|
+
this.tech = tech.shakaPlayer;
|
|
4
|
+
this.player = tech.el().player; // Store player reference for playback bitrate calculation
|
|
4
5
|
}
|
|
5
6
|
|
|
6
|
-
getSrc
|
|
7
|
+
getSrc(tech) {
|
|
7
8
|
try {
|
|
8
|
-
return this.tech.getManifestUri()
|
|
9
|
+
return this.tech.getManifestUri();
|
|
9
10
|
} catch (err) {}
|
|
10
|
-
return null
|
|
11
|
+
return null;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
getRenditionBitrate
|
|
14
|
+
getRenditionBitrate(tech) {
|
|
14
15
|
try {
|
|
15
|
-
return this.tech.getStats().streamBandwidth
|
|
16
|
-
} catch (err) {
|
|
17
|
-
return null
|
|
16
|
+
return this.tech.getStats().streamBandwidth;
|
|
17
|
+
} catch (err) {}
|
|
18
|
+
return null;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
getManifestBitrate() {
|
|
21
22
|
try {
|
|
22
|
-
|
|
23
|
+
// Return highest available bitrate from all variants
|
|
24
|
+
const tracks = this.tech.getVariantTracks();
|
|
25
|
+
if (tracks && tracks.length > 0) {
|
|
26
|
+
return Math.max(
|
|
27
|
+
...tracks.map((t) => t.videoBandwidth + (t.audioBandwidth || 0)),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getRenditionWidth(tech) {
|
|
35
|
+
try {
|
|
36
|
+
var tracks = this.tech.getVariantTracks();
|
|
23
37
|
for (var i in tracks) {
|
|
24
|
-
var track = tracks[i]
|
|
38
|
+
var track = tracks[i];
|
|
25
39
|
if (track.active && track.type === 'video') {
|
|
26
|
-
return track.width
|
|
40
|
+
return track.width;
|
|
27
41
|
}
|
|
28
42
|
}
|
|
29
|
-
} catch (err) {
|
|
30
|
-
return null
|
|
43
|
+
} catch (err) {}
|
|
44
|
+
return null;
|
|
31
45
|
}
|
|
32
46
|
|
|
33
|
-
getRenditionHeight
|
|
47
|
+
getRenditionHeight(tech) {
|
|
34
48
|
try {
|
|
35
|
-
var tracks = this.tech.getVariantTracks()
|
|
49
|
+
var tracks = this.tech.getVariantTracks();
|
|
36
50
|
for (var i in tracks) {
|
|
37
|
-
var track = tracks[i]
|
|
51
|
+
var track = tracks[i];
|
|
38
52
|
if (track.active && track.type === 'video') {
|
|
39
|
-
return track.height
|
|
53
|
+
return track.height;
|
|
40
54
|
}
|
|
41
55
|
}
|
|
42
|
-
} catch (err) {
|
|
43
|
-
return null
|
|
56
|
+
} catch (err) {}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getBitrate() {
|
|
61
|
+
// Calculate playback bitrate (actual content consumption rate)
|
|
62
|
+
return this.getContentBitratePlayback();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getContentBitratePlayback() {
|
|
66
|
+
try {
|
|
67
|
+
// Get the current variant's bitrate from manifest (streamBandwidth)
|
|
68
|
+
var stats = this.tech.getStats();
|
|
69
|
+
if (stats && stats.streamBandwidth && stats.streamBandwidth > 0) {
|
|
70
|
+
return stats.streamBandwidth;
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getSegmentDownloadBitrate() {
|
|
77
|
+
try {
|
|
78
|
+
// Use estimatedBandwidth for measured bitrate
|
|
79
|
+
var stats = this.tech.getStats();
|
|
80
|
+
if (stats && stats.estimatedBandwidth > 0) {
|
|
81
|
+
return stats.estimatedBandwidth;
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getNetworkDownloadBitrate() {
|
|
88
|
+
try {
|
|
89
|
+
// Shaka: use estimatedBandwidth for download bitrate (no separate property)
|
|
90
|
+
var stats = this.tech.getStats();
|
|
91
|
+
if (stats && stats.estimatedBandwidth > 0) {
|
|
92
|
+
return stats.estimatedBandwidth;
|
|
93
|
+
}
|
|
94
|
+
} catch (err) {}
|
|
95
|
+
return null;
|
|
44
96
|
}
|
|
45
97
|
}
|
|
46
98
|
|
|
47
99
|
ShakaTech.isUsing = function (tech) {
|
|
48
|
-
return !!tech.shakaPlayer
|
|
49
|
-
}
|
|
100
|
+
return !!tech.shakaPlayer;
|
|
101
|
+
};
|
package/src/tracker.js
CHANGED
|
@@ -12,6 +12,7 @@ import DaiAdsTracker from './ads/dai';
|
|
|
12
12
|
export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
13
13
|
constructor(player, options) {
|
|
14
14
|
super(player, options);
|
|
15
|
+
|
|
15
16
|
this.isContentEnd = false;
|
|
16
17
|
this.imaAdCuePoints = '';
|
|
17
18
|
this.daiInitialized = false;
|
|
@@ -112,176 +113,102 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
getBitrate() {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let totalBitrate = 0;
|
|
116
|
+
return this.getContentBitratePlayback();
|
|
117
|
+
}
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
// Measures: Actual content consumption rate during playback
|
|
120
|
+
getContentBitratePlayback() {
|
|
121
|
+
try {
|
|
120
122
|
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
currentMedia = tech.hls.playlists.media();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (currentMedia && currentMedia.attributes) {
|
|
135
|
-
if (currentMedia.attributes.BANDWIDTH) {
|
|
136
|
-
videoBitrate = currentMedia.attributes.BANDWIDTH;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Get audio bitrate if available
|
|
140
|
-
const audioTracks = this.player.audioTracks();
|
|
141
|
-
let activeAudioTrack;
|
|
142
|
-
|
|
143
|
-
if (audioTracks && audioTracks.length > 0) {
|
|
144
|
-
for (let i = 0; i < audioTracks.length; i++) {
|
|
145
|
-
if (audioTracks[i].enabled) {
|
|
146
|
-
activeAudioTrack = audioTracks[i];
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (activeAudioTrack && currentMedia.attributes.AUDIO) {
|
|
153
|
-
let masterPlaylist;
|
|
154
|
-
if (playlists) {
|
|
155
|
-
masterPlaylist = playlists.master || playlists.main;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
masterPlaylist &&
|
|
160
|
-
masterPlaylist.mediaGroups &&
|
|
161
|
-
masterPlaylist.mediaGroups.AUDIO
|
|
162
|
-
) {
|
|
163
|
-
const audioGroup =
|
|
164
|
-
masterPlaylist.mediaGroups.AUDIO[currentMedia.attributes.AUDIO];
|
|
165
|
-
const audioMediaInfo =
|
|
166
|
-
audioGroup && audioGroup[activeAudioTrack.id];
|
|
167
|
-
|
|
168
|
-
if (
|
|
169
|
-
audioMediaInfo &&
|
|
170
|
-
audioMediaInfo.playlists &&
|
|
171
|
-
audioMediaInfo.playlists[0]
|
|
172
|
-
) {
|
|
173
|
-
const audioPlaylist = audioMediaInfo.playlists[0].attributes;
|
|
174
|
-
if (audioPlaylist && audioPlaylist.BANDWIDTH) {
|
|
175
|
-
audioBitrate = audioPlaylist.BANDWIDTH;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
totalBitrate = videoBitrate + audioBitrate;
|
|
182
|
-
|
|
183
|
-
return totalBitrate; // Return in bps
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Method 2: Try Shaka Player
|
|
187
|
-
const shakaPlayer =
|
|
188
|
-
tech.shakaPlayer_ || tech.shaka_ || tech.shakaPlayer;
|
|
189
|
-
if (shakaPlayer && typeof shakaPlayer.getStats === 'function') {
|
|
190
|
-
const stats = shakaPlayer.getStats();
|
|
191
|
-
if (stats && stats.streamBandwidth) {
|
|
192
|
-
return stats.streamBandwidth;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Method 3: Try HLS.js
|
|
197
|
-
const hlsJs = tech.hls_;
|
|
198
|
-
if (hlsJs && hlsJs.levels && hlsJs.currentLevel >= 0) {
|
|
199
|
-
const currentLevel = hlsJs.levels[hlsJs.currentLevel];
|
|
200
|
-
if (currentLevel && currentLevel.bitrate) {
|
|
201
|
-
return currentLevel.bitrate;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
124
|
+
// 1. Get the current active rendition (The most accurate "Playback Bitrate")
|
|
125
|
+
if (tech?.vhs?.playlists?.media()) {
|
|
126
|
+
const activePlaylist = tech.vhs.playlists.media();
|
|
127
|
+
// Use AVERAGE-BANDWIDTH if available, fallback to BANDWIDTH
|
|
128
|
+
return (
|
|
129
|
+
activePlaylist.attributes['AVERAGE-BANDWIDTH'] ||
|
|
130
|
+
activePlaylist.attributes.BANDWIDTH ||
|
|
131
|
+
null
|
|
132
|
+
);
|
|
204
133
|
}
|
|
205
134
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
} else if (this.player.dash && this.player.dash.mediaPlayer) {
|
|
211
|
-
dashPlayer = this.player.dash.mediaPlayer;
|
|
135
|
+
// 2. Fallback to tech wrappers (Shaka/Hls.js) if they have a getBitrate method
|
|
136
|
+
const techWrapper = this.getTech();
|
|
137
|
+
if (techWrapper?.getBitrate) {
|
|
138
|
+
return techWrapper.getBitrate();
|
|
212
139
|
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
/* ignore */
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
213
145
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
) {
|
|
219
|
-
// Get audio bitrate
|
|
220
|
-
const audioQuality = dashPlayer.getQualityFor('audio');
|
|
221
|
-
const audioBitrateList = dashPlayer.getBitrateInfoListFor('audio');
|
|
222
|
-
if (
|
|
223
|
-
audioQuality !== undefined &&
|
|
224
|
-
audioBitrateList &&
|
|
225
|
-
audioBitrateList[audioQuality] &&
|
|
226
|
-
audioBitrateList[audioQuality].bitrate
|
|
227
|
-
) {
|
|
228
|
-
audioBitrate = audioBitrateList[audioQuality].bitrate;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Get video bitrate
|
|
232
|
-
const videoQuality = dashPlayer.getQualityFor('video');
|
|
233
|
-
const videoBitrateList = dashPlayer.getBitrateInfoListFor('video');
|
|
234
|
-
if (
|
|
235
|
-
videoQuality !== undefined &&
|
|
236
|
-
videoBitrateList &&
|
|
237
|
-
videoBitrateList[videoQuality] &&
|
|
238
|
-
videoBitrateList[videoQuality].bitrate
|
|
239
|
-
) {
|
|
240
|
-
videoBitrate = videoBitrateList[videoQuality].bitrate;
|
|
241
|
-
} else {
|
|
242
|
-
videoBitrate = videoBitrate || 0;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
totalBitrate = audioBitrate + videoBitrate;
|
|
246
|
-
return totalBitrate;
|
|
247
|
-
}
|
|
146
|
+
getRenditionName() {
|
|
147
|
+
let tech = this.getTech();
|
|
148
|
+
if (tech && tech.getRenditionName) {
|
|
149
|
+
return tech.getRenditionName();
|
|
248
150
|
}
|
|
151
|
+
}
|
|
249
152
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
) {
|
|
257
|
-
|
|
153
|
+
getManifestBitrate() {
|
|
154
|
+
try {
|
|
155
|
+
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
156
|
+
// tech.vhs.playlists.master.playlists contains the array of all renditions
|
|
157
|
+
const allRenditions = tech?.vhs?.playlists?.master?.playlists;
|
|
158
|
+
|
|
159
|
+
if (allRenditions && allRenditions.length > 0) {
|
|
160
|
+
// Find the highest BANDWIDTH value in the list
|
|
161
|
+
const maxBitrate = Math.max(
|
|
162
|
+
...allRenditions.map((p) => p.attributes.BANDWIDTH || 0),
|
|
163
|
+
);
|
|
164
|
+
return maxBitrate > 0 ? maxBitrate : null;
|
|
258
165
|
}
|
|
259
166
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
techWrapper.
|
|
264
|
-
) {
|
|
265
|
-
return techWrapper.tech.stats.bandwidth;
|
|
167
|
+
// Fallback to tech wrappers (Shaka/Hls.js)
|
|
168
|
+
const techWrapper = this.getTech();
|
|
169
|
+
if (techWrapper?.getManifestBitrate) {
|
|
170
|
+
return techWrapper.getManifestBitrate();
|
|
266
171
|
}
|
|
172
|
+
} catch (e) {
|
|
173
|
+
/* ignore */
|
|
267
174
|
}
|
|
268
|
-
|
|
269
175
|
return null;
|
|
270
176
|
}
|
|
271
177
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
178
|
+
getSegmentDownloadBitrate() {
|
|
179
|
+
try {
|
|
180
|
+
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
181
|
+
|
|
182
|
+
// VHS stats.bandwidth
|
|
183
|
+
if (tech?.vhs?.stats?.bandwidth && tech.vhs.stats.bandwidth > 0) {
|
|
184
|
+
return tech.vhs.stats.bandwidth;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Fallback to tech wrappers (Shaka/Hls.js)
|
|
188
|
+
const techWrapper = this.getTech();
|
|
189
|
+
if (techWrapper?.getSegmentDownloadBitrate) {
|
|
190
|
+
return techWrapper.getSegmentDownloadBitrate();
|
|
191
|
+
}
|
|
192
|
+
} catch (err) {
|
|
193
|
+
/* ignore */
|
|
276
194
|
}
|
|
195
|
+
return null;
|
|
277
196
|
}
|
|
278
197
|
|
|
279
|
-
|
|
280
|
-
|
|
198
|
+
getNetworkDownloadBitrate() {
|
|
199
|
+
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
|
200
|
+
|
|
201
|
+
if (tech?.vhs?.throughput && tech.vhs.throughput > 0) {
|
|
202
|
+
return tech.vhs.throughput;
|
|
203
|
+
}
|
|
281
204
|
|
|
282
|
-
|
|
283
|
-
|
|
205
|
+
// Fallback to tech wrapper implementation
|
|
206
|
+
const techWrapper = this.getTech();
|
|
207
|
+
if (techWrapper?.getNetworkDownloadBitrate) {
|
|
208
|
+
return techWrapper.getNetworkDownloadBitrate();
|
|
284
209
|
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
285
212
|
}
|
|
286
213
|
|
|
287
214
|
getRenditionHeight() {
|
|
@@ -364,7 +291,6 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
364
291
|
this.player.on('waiting', this.onWaiting);
|
|
365
292
|
this.player.on('timeupdate', this.onTimeupdate);
|
|
366
293
|
this.player.on('ads-allpods-completed', this.OnAdsAllpodsCompleted);
|
|
367
|
-
|
|
368
294
|
this.player.on('stream-manager', this.onStreamManager);
|
|
369
295
|
}
|
|
370
296
|
|
|
@@ -387,7 +313,6 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
|
|
|
387
313
|
this.player.off('waiting', this.onWaiting);
|
|
388
314
|
this.player.off('timeupdate', this.onTimeupdate);
|
|
389
315
|
this.player.off('ads-allpods-completed', this.OnAdsAllpodsCompleted);
|
|
390
|
-
|
|
391
316
|
this.player.off('stream-manager', this.onStreamManager);
|
|
392
317
|
}
|
|
393
318
|
|