@eluvio/elv-client-js 4.2.16 → 4.2.17
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/dist/ElvClient-min.js +1 -1
- package/dist/ElvClient-node-min.js +1 -1
- package/dist/ElvFrameClient-min.js +1 -1
- package/dist/ElvPermissionsClient-min.js +1 -1
- package/dist/ElvWalletClient-min.js +1 -1
- package/dist/ElvWalletClient-node-min.js +1 -1
- package/dist/src/AuthorizationClient.js +2 -1
- package/dist/src/ContentObjectAudit.js +2 -1
- package/dist/src/ContentObjectVerification.js +281 -0
- package/dist/src/ElvClient.js +8 -9
- package/dist/src/FrameClient.js +1 -1
- package/dist/src/HttpClient.js +83 -47
- package/dist/src/NetworkUrls.js +8 -0
- package/dist/src/abr_profiles/abr_profile_live_drm.js +0 -10
- package/dist/src/client/ContentAccess.js +76 -85
- package/dist/src/client/LiveConf.js +170 -84
- package/dist/src/client/LiveStream.js +5205 -2118
- package/dist/src/live_recording_config_profiles/live_recording_config_default.js +45 -0
- package/package.json +3 -2
- package/src/FrameClient.js +23 -2
- package/src/abr_profiles/abr_profile_live_drm.js +0 -10
- package/src/client/ContentAccess.js +1 -2
- package/src/client/LiveConf.js +149 -65
- package/src/client/LiveStream.js +2592 -654
- package/src/live_recording_config_profiles/live_recording_config_default.js +54 -0
- package/src/live_recording_config_profiles/live_stream_profile_full.json +143 -0
- package/testScripts/StreamUpdateLinks.js +95 -0
- package/utilities/LiveOutputs.js +149 -0
- package/utilities/StreamCreate.js +53 -0
- package/utilities/lib/helpers.js +5 -1
- package/utilities/tests/mocks/ElvClient.mock.js +9 -1
- package/utilities/tests/unit/StreamCreate.test.js +39 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var LiveRecordingConfigDefault = {
|
|
2
|
+
drm_type: "clear",
|
|
3
|
+
recording_config: {
|
|
4
|
+
part_ttl: 86400,
|
|
5
|
+
reconnect_timeout: 3600,
|
|
6
|
+
connection_timeout: 3600,
|
|
7
|
+
copy_mpegts: false
|
|
8
|
+
},
|
|
9
|
+
profile: {
|
|
10
|
+
ladder_specs: {
|
|
11
|
+
audio: [{
|
|
12
|
+
bit_rate: 192000,
|
|
13
|
+
channels: 2,
|
|
14
|
+
codecs: "mp4a.40.2"
|
|
15
|
+
}, {
|
|
16
|
+
bit_rate: 384000,
|
|
17
|
+
channels: 6,
|
|
18
|
+
codecs: "mp4a.40.2"
|
|
19
|
+
}],
|
|
20
|
+
video: [{
|
|
21
|
+
bit_rate: 9500000,
|
|
22
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
23
|
+
height: 1080,
|
|
24
|
+
width: 1920
|
|
25
|
+
}, {
|
|
26
|
+
bit_rate: 4500000,
|
|
27
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
28
|
+
height: 720,
|
|
29
|
+
width: 1280
|
|
30
|
+
}, {
|
|
31
|
+
bit_rate: 2000000,
|
|
32
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
33
|
+
height: 540,
|
|
34
|
+
width: 960
|
|
35
|
+
}, {
|
|
36
|
+
bit_rate: 900000,
|
|
37
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
38
|
+
height: 540,
|
|
39
|
+
width: 960
|
|
40
|
+
}]
|
|
41
|
+
},
|
|
42
|
+
name: "Default"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
module.exports = LiveRecordingConfigDefault;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eluvio/elv-client-js",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.17",
|
|
4
4
|
"description": "Javascript client for the Eluvio Content Fabric",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "Kevin Talmadge",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"lodash": "^4.17.19",
|
|
80
80
|
"lodash.isequal": "^4.5.0",
|
|
81
81
|
"mime-types": "^2.1.24",
|
|
82
|
+
"moment": "^2.27.0",
|
|
82
83
|
"multihashes": "^0.4.14",
|
|
83
84
|
"node-fetch": "^2.6.7",
|
|
84
85
|
"object-path": "^0.11.8",
|
|
@@ -112,7 +113,7 @@
|
|
|
112
113
|
"eslint": "^9.32.0",
|
|
113
114
|
"jsdoc": "^4.0.0",
|
|
114
115
|
"jsdom": "^26.1.0",
|
|
115
|
-
"
|
|
116
|
+
"mocha": "^11.7.5",
|
|
116
117
|
"raw-loader": "^0.5.1",
|
|
117
118
|
"shell-quote": "^1.7.2",
|
|
118
119
|
"showdown": "^1.9.1",
|
package/src/FrameClient.js
CHANGED
|
@@ -447,6 +447,15 @@ class FrameClient {
|
|
|
447
447
|
"Nodes",
|
|
448
448
|
"NTPInstance",
|
|
449
449
|
"ObjectCleanup",
|
|
450
|
+
"OutputsCreate",
|
|
451
|
+
"OutputsDelete",
|
|
452
|
+
"OutputsList",
|
|
453
|
+
"OutputsListItem",
|
|
454
|
+
"OutputsModify",
|
|
455
|
+
"OutputsModifyBatch",
|
|
456
|
+
"OutputsResolveSrtPullUrls",
|
|
457
|
+
"OutputsState",
|
|
458
|
+
"OutputsStop",
|
|
450
459
|
"Permission",
|
|
451
460
|
"PlayoutOptions",
|
|
452
461
|
"PlayoutPathResolution",
|
|
@@ -472,6 +481,7 @@ class FrameClient {
|
|
|
472
481
|
"RevokeShare",
|
|
473
482
|
"SendFunds",
|
|
474
483
|
"SetAccessCharge",
|
|
484
|
+
"StreamApplyProfile",
|
|
475
485
|
"SetAuth",
|
|
476
486
|
"SetAuthContext",
|
|
477
487
|
"SetAuthPolicy",
|
|
@@ -496,17 +506,28 @@ class FrameClient {
|
|
|
496
506
|
"SpaceNodes",
|
|
497
507
|
"StartABRMezzanineJobs",
|
|
498
508
|
"StreamAddWatermark",
|
|
509
|
+
"StreamApplyProfile",
|
|
510
|
+
"StreamAssignProfile",
|
|
499
511
|
"StreamConfig",
|
|
512
|
+
"StreamConfigProfile",
|
|
513
|
+
"StreamConfigProfiles",
|
|
500
514
|
"StreamCopyToVod",
|
|
501
515
|
"StreamCreate",
|
|
502
516
|
"StreamInitialize",
|
|
503
517
|
"StreamInsertion",
|
|
518
|
+
"StreamLinkToSite",
|
|
504
519
|
"StreamListUrls",
|
|
520
|
+
"StreamRemoveLinkToSite",
|
|
505
521
|
"StreamRemoveWatermark",
|
|
522
|
+
"StreamSaveConfigProfile",
|
|
506
523
|
"StreamSetOfferingAndDRM",
|
|
507
|
-
"
|
|
524
|
+
"StreamSiteSettings",
|
|
525
|
+
"StreamStartRecording",
|
|
508
526
|
"StreamStartOrStopOrReset",
|
|
509
|
-
"
|
|
527
|
+
"StreamStatus",
|
|
528
|
+
"StreamStopRecording",
|
|
529
|
+
"StreamUnassignProfile",
|
|
530
|
+
"StreamUpdateConfig",
|
|
510
531
|
"SuspendNTPInstance",
|
|
511
532
|
"TenantContractId",
|
|
512
533
|
"TenantId",
|
|
@@ -1863,16 +1863,6 @@ const AbrProfileLiveDrm = {
|
|
|
1863
1863
|
}
|
|
1864
1864
|
},
|
|
1865
1865
|
"playout_formats": {
|
|
1866
|
-
"dash-playready-cenc": {
|
|
1867
|
-
"drm": {
|
|
1868
|
-
"enc_scheme_name": "cenc",
|
|
1869
|
-
"type": "DrmPlayReady"
|
|
1870
|
-
},
|
|
1871
|
-
"protocol": {
|
|
1872
|
-
"min_buffer_length": 2,
|
|
1873
|
-
"type": "ProtoDash"
|
|
1874
|
-
}
|
|
1875
|
-
},
|
|
1876
1866
|
"dash-widevine": {
|
|
1877
1867
|
"drm": {
|
|
1878
1868
|
"content_id": "",
|
|
@@ -1804,7 +1804,6 @@ exports.GlobalUrl = async function({
|
|
|
1804
1804
|
}
|
|
1805
1805
|
}
|
|
1806
1806
|
|
|
1807
|
-
console.log("Updated")
|
|
1808
1807
|
let urlPath = UrlJoin("s", network);
|
|
1809
1808
|
if(versionHash) {
|
|
1810
1809
|
objectId = this.utils.DecodeVersionHash(versionHash).objectId;
|
|
@@ -2824,7 +2823,7 @@ exports.EncryptionConk = async function({libraryId, objectId, versionHash, write
|
|
|
2824
2823
|
const owner = await this.authClient.Owner({id: objectId});
|
|
2825
2824
|
|
|
2826
2825
|
const ownerCapKey = `eluv.caps.iusr${this.utils.AddressToHash(this.signer.address)}`;
|
|
2827
|
-
const ownerCap = await this.ContentObjectMetadata({libraryId, objectId, versionHash, metadataSubtree: ownerCapKey});
|
|
2826
|
+
const ownerCap = await this.ContentObjectMetadata({libraryId, objectId, versionHash, writeToken, metadataSubtree: ownerCapKey});
|
|
2828
2827
|
|
|
2829
2828
|
if(!this.utils.EqualAddress(owner, this.signer.address) && !ownerCap) {
|
|
2830
2829
|
if(download) {
|
package/src/client/LiveConf.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint no-console: 0 */
|
|
2
|
+
const R = require("ramda");
|
|
2
3
|
|
|
3
4
|
const DefaultABRLadder = {
|
|
4
|
-
"video"
|
|
5
|
+
"video": [
|
|
5
6
|
{
|
|
6
7
|
bit_rate: 14000000,
|
|
7
8
|
codecs: "avc1.640028,mp4a.40.2",
|
|
@@ -33,7 +34,7 @@ const DefaultABRLadder = {
|
|
|
33
34
|
width: 960
|
|
34
35
|
}
|
|
35
36
|
],
|
|
36
|
-
"audio"
|
|
37
|
+
"audio": [
|
|
37
38
|
{
|
|
38
39
|
bit_rate: 192000,
|
|
39
40
|
channels: 2,
|
|
@@ -55,8 +56,9 @@ const LiveconfTemplate = {
|
|
|
55
56
|
},
|
|
56
57
|
playout_config: {
|
|
57
58
|
dvr_enabled: true,
|
|
58
|
-
dvr_max_duration:
|
|
59
|
+
dvr_max_duration: 14400,
|
|
59
60
|
rebroadcast_start_time_sec_epoch: 0,
|
|
61
|
+
playout_sharding_level: 2,
|
|
60
62
|
vod_enabled: false
|
|
61
63
|
},
|
|
62
64
|
recording_config: {
|
|
@@ -64,11 +66,11 @@ const LiveconfTemplate = {
|
|
|
64
66
|
description: "",
|
|
65
67
|
ladder_specs: [],
|
|
66
68
|
listen: true,
|
|
67
|
-
live_delay_nano:
|
|
69
|
+
live_delay_nano: 6000000000,
|
|
68
70
|
max_duration_sec: -1,
|
|
69
71
|
name: "",
|
|
70
72
|
origin_url: "",
|
|
71
|
-
part_ttl:
|
|
73
|
+
part_ttl: 86400,
|
|
72
74
|
playout_type: "live",
|
|
73
75
|
source_timescale: null,
|
|
74
76
|
reconnect_timeout: 600,
|
|
@@ -113,18 +115,49 @@ const LiveconfTemplate = {
|
|
|
113
115
|
};
|
|
114
116
|
|
|
115
117
|
class LiveConf {
|
|
116
|
-
constructor(
|
|
118
|
+
constructor({
|
|
119
|
+
url,
|
|
120
|
+
probeData,
|
|
121
|
+
nodeId,
|
|
122
|
+
nodeUrl,
|
|
123
|
+
includeAVSegDurations,
|
|
124
|
+
overwriteOriginUrl,
|
|
125
|
+
syncAudioToVideo,
|
|
126
|
+
liveRecordingMeta
|
|
127
|
+
}) {
|
|
128
|
+
this.url = url;
|
|
117
129
|
this.probeData = probeData;
|
|
118
130
|
this.nodeId = nodeId;
|
|
119
131
|
this.nodeUrl = nodeUrl;
|
|
120
132
|
this.includeAVSegDurations = includeAVSegDurations;
|
|
121
133
|
this.overwriteOriginUrl = overwriteOriginUrl;
|
|
122
134
|
this.syncAudioToVideo = syncAudioToVideo;
|
|
135
|
+
this.currentLiveRecordingMeta = liveRecordingMeta;
|
|
123
136
|
}
|
|
124
137
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
getFormat() {
|
|
139
|
+
if (this.probeData.format.format_name) {
|
|
140
|
+
return this.probeData.format.format_name;
|
|
141
|
+
}
|
|
142
|
+
const fileNameSplit = this.probeData.format?.filename?.split(":");
|
|
143
|
+
if (fileNameSplit.length > 1) {
|
|
144
|
+
const protoScheme = fileNameSplit[0];
|
|
145
|
+
switch(protoScheme) {
|
|
146
|
+
case "rtmp":
|
|
147
|
+
return "flv";
|
|
148
|
+
case "udp":
|
|
149
|
+
case "rtp":
|
|
150
|
+
case "srt":
|
|
151
|
+
return "mpegts";
|
|
152
|
+
default:
|
|
153
|
+
return "format_unknown";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getProtocol() {
|
|
159
|
+
const protoScheme = this.url.split(":")[0];
|
|
160
|
+
return protoScheme;
|
|
128
161
|
}
|
|
129
162
|
|
|
130
163
|
getStreamDataForCodecType(codecType) {
|
|
@@ -139,16 +172,18 @@ class LiveConf {
|
|
|
139
172
|
|
|
140
173
|
// Return all audio streams found in the probe
|
|
141
174
|
// Used by generateAudioStreamsConfig()
|
|
142
|
-
getAudioStreamsFromProbe() {
|
|
143
|
-
|
|
175
|
+
getAudioStreamsFromProbe({ladderProfile}) {
|
|
176
|
+
const audioStreams = {};
|
|
144
177
|
const audioStreamData = this.probeData.streams.filter((value) => value.codec_type === "audio");
|
|
145
178
|
|
|
146
179
|
for(let index = 0; index < audioStreamData.length; index++) {
|
|
147
180
|
const currentStreamIndex = audioStreamData[index].stream_index;
|
|
148
181
|
const currentStreamData = audioStreamData[index];
|
|
149
182
|
|
|
183
|
+
const profileAudioForType = ladderProfile?.audio?.find(a => a.channels === currentStreamData.channels);
|
|
184
|
+
|
|
150
185
|
audioStreams[currentStreamIndex] = {
|
|
151
|
-
recordingBitrate: Math.max(currentStreamData.bit_rate,
|
|
186
|
+
recordingBitrate: profileAudioForType?.bit_rate ?? Math.max(currentStreamData.bit_rate ?? 0, 12800),
|
|
152
187
|
recordingChannels: currentStreamData.channels,
|
|
153
188
|
playoutLabel: `Audio ${index + 1}`
|
|
154
189
|
};
|
|
@@ -205,16 +240,15 @@ class LiveConf {
|
|
|
205
240
|
calcSegDuration({sourceTimescale, sampleRate, audioCodec}) {
|
|
206
241
|
let seg = {};
|
|
207
242
|
|
|
208
|
-
switch(this.
|
|
209
|
-
case "
|
|
243
|
+
switch(this.getFormat()) {
|
|
244
|
+
case "flv":
|
|
210
245
|
seg = this.calcSegDurationRtmp({sourceTimescale, sampleRate, audioCodec});
|
|
211
246
|
break;
|
|
212
|
-
case "
|
|
213
|
-
case "srt":
|
|
247
|
+
case "mpegts":
|
|
214
248
|
seg = this.calcSegDurationMpegts({sourceTimescale, sampleRate, audioCodec});
|
|
215
249
|
break;
|
|
216
250
|
default:
|
|
217
|
-
throw "protocol not supported - " + this.
|
|
251
|
+
throw "protocol not supported - " + this.getFormat();
|
|
218
252
|
}
|
|
219
253
|
|
|
220
254
|
if(audioCodec == "aac") {
|
|
@@ -362,32 +396,79 @@ class LiveConf {
|
|
|
362
396
|
syncAudioToStreamIdValue() {
|
|
363
397
|
let sync_id = -1;
|
|
364
398
|
let videoStream = this.getStreamDataForCodecType("video");
|
|
365
|
-
switch(this.
|
|
366
|
-
case "
|
|
367
|
-
case "srt":
|
|
399
|
+
switch(this.getFormat()) {
|
|
400
|
+
case "mpegts":
|
|
368
401
|
sync_id = videoStream.stream_id;
|
|
369
402
|
break;
|
|
370
|
-
case "
|
|
403
|
+
case "flv":
|
|
371
404
|
sync_id = videoStream.stream_index;
|
|
372
405
|
break;
|
|
373
406
|
}
|
|
374
407
|
return sync_id;
|
|
375
408
|
}
|
|
376
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Map custom live recording profile to the expected config structure
|
|
412
|
+
* @param {Object} customProfile - User's custom recording profile
|
|
413
|
+
* @return {Object} - Mapped config in live_recording format
|
|
414
|
+
*/
|
|
415
|
+
MapCustomProfileToLiveConfig({customProfile}) {
|
|
416
|
+
if(!customProfile) return {};
|
|
417
|
+
|
|
418
|
+
const CompactDeep = (obj) => {
|
|
419
|
+
if(obj === null || typeof obj !== "object" || Array.isArray(obj)) {
|
|
420
|
+
return obj;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return R.pipe(
|
|
424
|
+
R.reject(R.isNil),
|
|
425
|
+
R.map(val => typeof val === "object" ? CompactDeep(val) : val)
|
|
426
|
+
)(obj);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const {recording_config, recording_params, ...rest} = customProfile;
|
|
430
|
+
|
|
431
|
+
return CompactDeep({
|
|
432
|
+
live_recording: {
|
|
433
|
+
...rest,
|
|
434
|
+
recording_config: {
|
|
435
|
+
recording_params: {
|
|
436
|
+
...recording_params,
|
|
437
|
+
part_ttl: recording_config?.part_ttl,
|
|
438
|
+
reconnect_timeout: recording_config?.reconnect_timeout,
|
|
439
|
+
xc_params: {
|
|
440
|
+
...(recording_params?.xc_params || {}),
|
|
441
|
+
connection_timeout: recording_config?.connection_timeout,
|
|
442
|
+
copy_mpegts: recording_config?.copy_mpegts,
|
|
443
|
+
input_cfg: recording_config?.input_cfg
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
377
451
|
/*
|
|
378
452
|
* Generate audio streams recording configuration based on the optional custom settings.
|
|
379
453
|
* If no custom "audio" section is present, record all the acceptable audio streams found in the probe
|
|
380
454
|
*/
|
|
381
|
-
generateAudioStreamsConfig({
|
|
455
|
+
generateAudioStreamsConfig({liveRecordingConfigProfile}) {
|
|
456
|
+
const ladderProfile = liveRecordingConfigProfile?.playout_config?.ladder_specs || DefaultABRLadder;
|
|
457
|
+
const audioSettings = liveRecordingConfigProfile?.recording_stream_config?.audio;
|
|
382
458
|
|
|
383
459
|
let audioStreams = {};
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
460
|
+
|
|
461
|
+
if(audioSettings && Object.keys(audioSettings).length > 0) {
|
|
462
|
+
for(let i = 0; i < Object.keys(audioSettings).length; i ++) {
|
|
463
|
+
const audioIdx = Object.keys(audioSettings)[i];
|
|
464
|
+
const audio = audioSettings[audioIdx];
|
|
465
|
+
const profileAudioForType = ladderProfile?.audio.find(a => a.channels === (audio.channels ?? 2));
|
|
466
|
+
|
|
388
467
|
audioStreams[audioIdx] = {
|
|
389
|
-
recordingBitrate: audio.recording_bitrate
|
|
468
|
+
recordingBitrate: ladderProfile?.audio ? profileAudioForType.bit_rate : audio.recording_bitrate ?? 192000,
|
|
390
469
|
recordingChannels: audio.recording_channels || 2,
|
|
470
|
+
lang: audio.lang,
|
|
471
|
+
isDefault: audio.default
|
|
391
472
|
};
|
|
392
473
|
if(audio.playout) {
|
|
393
474
|
audioStreams[audioIdx].playoutLabel = audio.playout_label || `Audio ${i + 1}`;
|
|
@@ -396,21 +477,34 @@ class LiveConf {
|
|
|
396
477
|
}
|
|
397
478
|
|
|
398
479
|
// If no audio streams specified in custom config, set up all the suitable audio streams in the probe
|
|
399
|
-
if(!
|
|
400
|
-
audioStreams = this.getAudioStreamsFromProbe();
|
|
480
|
+
if(!audioSettings || Object.keys(audioSettings).length === 0) {
|
|
481
|
+
audioStreams = this.getAudioStreamsFromProbe({ladderProfile});
|
|
401
482
|
}
|
|
402
483
|
|
|
403
484
|
return audioStreams;
|
|
404
485
|
}
|
|
405
486
|
|
|
406
487
|
/*
|
|
407
|
-
* Generate the live recording config as required by QFAB, based on defaults and optional custom settings.
|
|
488
|
+
* Generate the live recording config as required by QFAB, based on defaults, existing settings and optional custom settings.
|
|
408
489
|
*/
|
|
409
490
|
generateLiveConf({customSettings}) {
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
491
|
+
// Saved config overrides defaults and is preserved on reconfiguration
|
|
492
|
+
let conf = R.clone(LiveconfTemplate);
|
|
493
|
+
|
|
494
|
+
if(this.currentLiveRecordingMeta) {
|
|
495
|
+
conf = R.mergeDeepRight(conf,
|
|
496
|
+
{live_recording: this.currentLiveRecordingMeta});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if(customSettings.liveRecordingConfigProfile) {
|
|
500
|
+
conf = R.mergeDeepRight(conf,
|
|
501
|
+
this.MapCustomProfileToLiveConfig({
|
|
502
|
+
customProfile: customSettings.liveRecordingConfigProfile
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const fileName = this.url;
|
|
507
|
+
const audioStreams = this.generateAudioStreamsConfig({liveRecordingConfigProfile: customSettings.liveRecordingConfigProfile});
|
|
414
508
|
|
|
415
509
|
// Retrieve one audio stream from the probe to read the sample rate and codec name
|
|
416
510
|
const audioStream = this.getStreamDataForCodecType("audio");
|
|
@@ -440,15 +534,10 @@ class LiveConf {
|
|
|
440
534
|
}
|
|
441
535
|
|
|
442
536
|
// Fill in specifics for protocol
|
|
443
|
-
switch(this.
|
|
444
|
-
case "
|
|
445
|
-
sourceTimescale = 90000;
|
|
446
|
-
conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
|
|
447
|
-
break;
|
|
448
|
-
case "srt":
|
|
537
|
+
switch(this.getFormat()) {
|
|
538
|
+
case "mpegts":
|
|
449
539
|
sourceTimescale = 90000;
|
|
450
540
|
conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
|
|
451
|
-
conf.live_recording.recording_config.recording_params.live_delay_nano = 4000000000;
|
|
452
541
|
break;
|
|
453
542
|
case "rtmp":
|
|
454
543
|
sourceTimescale = 16000;
|
|
@@ -458,7 +547,15 @@ class LiveConf {
|
|
|
458
547
|
console.log("HLS detected. Not yet implemented");
|
|
459
548
|
break;
|
|
460
549
|
default:
|
|
461
|
-
console.log("Unsupported media", this.
|
|
550
|
+
console.log("Unsupported media", this.getFormat());
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
switch(this.getProtocol()) {
|
|
555
|
+
case "srt":
|
|
556
|
+
if(!customSettings.liveRecordingConfigProfile?.recording_params?.live_delay_nano) {
|
|
557
|
+
conf.live_recording.recording_config.recording_params.live_delay_nano = 6000000000;
|
|
558
|
+
}
|
|
462
559
|
break;
|
|
463
560
|
}
|
|
464
561
|
|
|
@@ -481,11 +578,15 @@ class LiveConf {
|
|
|
481
578
|
conf.live_recording.recording_config.recording_params.xc_params.video_frame_duration_ts = segDurations.videoFrameDurationTs;
|
|
482
579
|
}
|
|
483
580
|
|
|
484
|
-
const
|
|
581
|
+
const ladder_specs = customSettings.liveRecordingConfigProfile?.playout_config?.ladder_specs;
|
|
582
|
+
const ladderProfile = ladder_specs?.video?.length > 0 ? ladder_specs : DefaultABRLadder;
|
|
485
583
|
|
|
486
584
|
conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
|
|
487
585
|
conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
|
|
488
586
|
|
|
587
|
+
// Reset ladder specs (updating existing stream will carry over old specs
|
|
588
|
+
conf.live_recording.recording_config.recording_params.ladder_specs = [];
|
|
589
|
+
|
|
489
590
|
// Determine video recording bitrate and ABR ladder
|
|
490
591
|
let topLadderRate = 0;
|
|
491
592
|
for(let i = 0; i < ladderProfile.video.length; i ++) {
|
|
@@ -521,6 +622,7 @@ class LiveConf {
|
|
|
521
622
|
break;
|
|
522
623
|
}
|
|
523
624
|
}
|
|
625
|
+
|
|
524
626
|
if(Object.keys(audioLadderSpec).length === 0) {
|
|
525
627
|
// If no channels layout match, just use the first element in the ladder
|
|
526
628
|
audioLadderSpec = {...ladderProfile.audio[0]};
|
|
@@ -532,8 +634,10 @@ class LiveConf {
|
|
|
532
634
|
audioLadderSpec.stream_name = `audio_${audioIndex}`;
|
|
533
635
|
audioLadderSpec.stream_label = audio.playoutLabel ? audio.playoutLabel : null;
|
|
534
636
|
audioLadderSpec.media_type = 2;
|
|
637
|
+
audioLadderSpec.lang = audio.lang ?? "";
|
|
535
638
|
|
|
536
|
-
|
|
639
|
+
|
|
640
|
+
if(Object.keys(audioStreams).length === 1 || audio.isDefault) {
|
|
537
641
|
audioLadderSpec.default = true;
|
|
538
642
|
}
|
|
539
643
|
|
|
@@ -541,33 +645,13 @@ class LiveConf {
|
|
|
541
645
|
if(audio.recordingBitrate > globalAudioBitrate) {
|
|
542
646
|
globalAudioBitrate = audio.recordingBitrate;
|
|
543
647
|
}
|
|
544
|
-
nAudio
|
|
648
|
+
nAudio++;
|
|
545
649
|
}
|
|
546
650
|
|
|
547
651
|
// Global recording bitrate for all audio streams
|
|
548
652
|
conf.live_recording.recording_config.recording_params.xc_params.audio_bitrate = globalAudioBitrate;
|
|
549
653
|
conf.live_recording.recording_config.recording_params.xc_params.n_audio = nAudio;
|
|
550
654
|
|
|
551
|
-
// Iterate through custom settings (which will override any existing setting)
|
|
552
|
-
function SetByPath({obj, path, value}) {
|
|
553
|
-
const keys = path.split(".");
|
|
554
|
-
let temp = obj;
|
|
555
|
-
for(let i = 0; i < keys.length - 1; i++) {
|
|
556
|
-
if(!temp[keys[i]]) {
|
|
557
|
-
temp[keys[i]] = {};
|
|
558
|
-
}
|
|
559
|
-
temp = temp[keys[i]];
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
temp[keys[keys.length - 1]] = value;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const {metaPathValues} = customSettings;
|
|
566
|
-
|
|
567
|
-
for(let [path, value] of Object.entries(metaPathValues || {})) {
|
|
568
|
-
SetByPath({obj: conf, path, value});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
655
|
return conf;
|
|
572
656
|
}
|
|
573
657
|
}
|