@newrelic/video-videojs 4.0.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/video-videojs",
3
- "version": "4.0.3",
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",
@@ -1,83 +1,95 @@
1
1
  export default class ContribHlsTech {
2
- constructor (tech) {
3
- this.tech = tech.vhs
4
- this.player = tech.el().player // Store player reference for playback bitrate calculation
2
+ constructor(tech) {
3
+ this.tech = tech.vhs;
4
+ this.player = tech.el().player; // Store player reference for playback bitrate calculation
5
5
  }
6
6
 
7
- getRenditionName () {
7
+ getRenditionName() {
8
8
  try {
9
- var media = this.tech.playlists.media()
10
- if (media && media.attributes) return media.attributes.NAME
11
- } catch (err) { }
12
- return null
9
+ var media = this.tech.playlists.media();
10
+ if (media && media.attributes) return media.attributes.NAME;
11
+ } catch (err) {}
12
+ return null;
13
13
  }
14
14
 
15
- getRenditionBitrate () {
15
+ getRenditionBitrate() {
16
16
  try {
17
- var media = this.tech.playlists.media()
18
- if (media && media.attributes) return media.attributes.BANDWIDTH
19
- } catch (err) { }
20
- return null
17
+ var media = this.tech.playlists.media();
18
+ if (media && media.attributes) return media.attributes.BANDWIDTH;
19
+ } catch (err) {}
20
+ return null;
21
21
  }
22
22
 
23
- getRenditionWidth () {
23
+ getRenditionWidth() {
24
24
  try {
25
- var media = this.tech.playlists.media()
25
+ var media = this.tech.playlists.media();
26
26
  if (media && media.attributes && media.attributes.RESOLUTION) {
27
- return media.attributes.RESOLUTION.width
27
+ return media.attributes.RESOLUTION.width;
28
28
  }
29
- } catch (err) { }
30
- return null
29
+ } catch (err) {}
30
+ return null;
31
31
  }
32
32
 
33
- getRenditionHeight () {
33
+ getRenditionHeight() {
34
34
  try {
35
- var media = this.tech.playlists.media()
35
+ var media = this.tech.playlists.media();
36
36
  if (media && media.attributes && media.attributes.RESOLUTION) {
37
- return media.attributes.RESOLUTION.height
37
+ return media.attributes.RESOLUTION.height;
38
38
  }
39
- } catch (err) { }
40
- return null
39
+ } catch (err) {}
40
+ return null;
41
41
  }
42
42
 
43
- getBitrate () {
43
+ getBitrate() {
44
44
  // Calculate playback bitrate (actual content consumption rate)
45
- return this.getContentBitratePlayback()
45
+ return this.getContentBitratePlayback();
46
46
  }
47
47
 
48
- getContentBitratePlayback () {
48
+ getManifestBitrate() {
49
49
  try {
50
- // VHS provides stats for calculating actual playback bitrate
51
- if (this.tech.stats && this.player) {
52
- const stats = this.tech.stats
53
-
54
- // Calculate actual playback bitrate based on content consumption
55
- if (stats.mediaBytesTransferred > 0 && stats.mediaRequests > 0) {
56
- const currentTime = this.player.currentTime() // in seconds
57
- const playbackRate = this.player.playbackRate() || 1
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
58
 
59
- // Adjust playback time by playback rate
60
- const effectivePlaybackTime = currentTime / playbackRate
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
+ }
61
68
 
62
- if (effectivePlaybackTime > 0) {
63
- const bitsTransferred = stats.mediaBytesTransferred * 8
64
- return bitsTransferred / effectivePlaybackTime
65
- }
66
- }
67
- }
69
+ getNetworkDownloadBitrate() {
70
+ if (this.tech.throughput !== undefined && this.tech.throughput > 0) {
71
+ return this.tech.throughput;
72
+ }
73
+ return null;
74
+ }
68
75
 
69
- // Fallback to VHS bandwidth estimates if stats not available
70
- if (this.tech.bandwidth !== undefined && this.tech.bandwidth > 0) {
71
- return this.tech.bandwidth
72
- }
73
- if (this.tech.systemBandwidth !== undefined && this.tech.systemBandwidth > 0) {
74
- return this.tech.systemBandwidth
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
+ );
75
87
  }
76
- } catch (err) { }
77
- return null
88
+ } catch (err) {}
89
+ return null;
78
90
  }
79
91
  }
80
92
 
81
93
  ContribHlsTech.isUsing = function (tech) {
82
- return !!tech.vhs
83
- }
94
+ return !!tech.vhs;
95
+ };
@@ -47,23 +47,40 @@ export default class HlsJs {
47
47
 
48
48
  getContentBitratePlayback() {
49
49
  try {
50
- if (this.tech.stats && this.player) {
51
- const stats = this.tech.stats;
52
- // Calculate actual playback bitrate based on content consumption
53
- if (stats.mediaBytesTransferred > 0 && stats.mediaRequests > 0) {
54
- const currentTime = this.player.currentTime(); // in seconds
55
- const playbackRate = this.player.playbackRate() || 1;
56
- const effectivePlaybackTime = currentTime / playbackRate;
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
+ }
57
58
 
58
- if (effectivePlaybackTime > 0) {
59
- const bitsTransferred = stats.mediaBytesTransferred * 8;
60
- return bitsTransferred / effectivePlaybackTime;
61
- }
62
- }
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));
63
64
  }
64
65
  } catch (err) {}
65
66
  return null;
66
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;
83
+ }
67
84
  }
68
85
 
69
86
  HlsJs.isUsing = function (tech) {
@@ -18,6 +18,19 @@ export default class ShakaTech {
18
18
  return null;
19
19
  }
20
20
 
21
+ getManifestBitrate() {
22
+ try {
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
+
21
34
  getRenditionWidth(tech) {
22
35
  try {
23
36
  var tracks = this.tech.getVariantTracks();
@@ -51,18 +64,32 @@ export default class ShakaTech {
51
64
 
52
65
  getContentBitratePlayback() {
53
66
  try {
67
+ // Get the current variant's bitrate from manifest (streamBandwidth)
54
68
  var stats = this.tech.getStats();
55
- if (stats) {
56
- // Shaka's estimatedBandwidth is the actual measured network bandwidth
57
- // This is the closest equivalent to playback bitrate calculation
58
- if (stats.estimatedBandwidth && stats.estimatedBandwidth > 0) {
59
- return stats.estimatedBandwidth;
60
- }
69
+ if (stats && stats.streamBandwidth && stats.streamBandwidth > 0) {
70
+ return stats.streamBandwidth;
71
+ }
72
+ } catch (err) {}
73
+ return null;
74
+ }
61
75
 
62
- // Fallback to streamBandwidth (bitrate of current variant from manifest)
63
- if (stats.streamBandwidth && stats.streamBandwidth > 0) {
64
- return stats.streamBandwidth;
65
- }
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;
66
93
  }
67
94
  } catch (err) {}
68
95
  return null;
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;
@@ -117,37 +118,28 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
117
118
 
118
119
  // Measures: Actual content consumption rate during playback
119
120
  getContentBitratePlayback() {
120
- const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
121
-
122
- // VHS/HLS support - calculate actual playback bitrate
123
- if (tech?.vhs?.stats) {
124
- const stats = tech.vhs.stats;
125
-
126
- // Calculate actual playback bitrate based on content consumption
127
- // This represents the bitrate at which content is being played back
128
- if (stats.mediaBytesTransferred > 0 && stats.mediaRequests > 0) {
129
- // Get total playback time (accounting for playback rate)
130
- const currentTime = this.player.currentTime(); // in seconds
131
- const playbackRate = this.player.playbackRate() || 1;
132
-
133
- // Adjust playback time by playback rate
134
- // If playing at 2x speed, you consume 2x the bitrate
135
- const effectivePlaybackTime = currentTime / playbackRate;
136
-
137
- if (effectivePlaybackTime > 0) {
138
- const bitsTransferred = stats.mediaBytesTransferred * 8;
139
- const playbackBitrate = bitsTransferred / effectivePlaybackTime;
140
- return playbackBitrate;
141
- }
121
+ try {
122
+ const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
123
+
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
+ );
142
133
  }
143
- }
144
134
 
145
- // Support for other tech implementations using the same formula
146
- const techWrapper = this.getTech();
147
- if (techWrapper?.getBitrate) {
148
- return techWrapper.getBitrate();
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();
139
+ }
140
+ } catch (err) {
141
+ /* ignore */
149
142
  }
150
-
151
143
  return null;
152
144
  }
153
145
 
@@ -158,14 +150,65 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
158
150
  }
159
151
  }
160
152
 
161
- getRenditionBitrate() {
162
- // Returns the target bitrate from the manifest (static, changes only on rendition switch)
163
- // This is different from getBitrate() which returns the actual measured bitrate
164
- let tech = this.getTech();
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;
165
+ }
166
+
167
+ // Fallback to tech wrappers (Shaka/Hls.js)
168
+ const techWrapper = this.getTech();
169
+ if (techWrapper?.getManifestBitrate) {
170
+ return techWrapper.getManifestBitrate();
171
+ }
172
+ } catch (e) {
173
+ /* ignore */
174
+ }
175
+ return null;
176
+ }
177
+
178
+ getSegmentDownloadBitrate() {
179
+ try {
180
+ const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
165
181
 
166
- if (tech && tech.getRenditionBitrate) {
167
- return tech.getRenditionBitrate();
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 */
194
+ }
195
+ return null;
196
+ }
197
+
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
+ }
204
+
205
+ // Fallback to tech wrapper implementation
206
+ const techWrapper = this.getTech();
207
+ if (techWrapper?.getNetworkDownloadBitrate) {
208
+ return techWrapper.getNetworkDownloadBitrate();
168
209
  }
210
+
211
+ return null;
169
212
  }
170
213
 
171
214
  getRenditionHeight() {
@@ -248,7 +291,6 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
248
291
  this.player.on('waiting', this.onWaiting);
249
292
  this.player.on('timeupdate', this.onTimeupdate);
250
293
  this.player.on('ads-allpods-completed', this.OnAdsAllpodsCompleted);
251
-
252
294
  this.player.on('stream-manager', this.onStreamManager);
253
295
  }
254
296
 
@@ -271,7 +313,6 @@ export default class VideojsTracker extends nrvideo.VideoTracker {
271
313
  this.player.off('waiting', this.onWaiting);
272
314
  this.player.off('timeupdate', this.onTimeupdate);
273
315
  this.player.off('ads-allpods-completed', this.OnAdsAllpodsCompleted);
274
-
275
316
  this.player.off('stream-manager', this.onStreamManager);
276
317
  }
277
318