@eluvio/elv-client-js 4.0.47 → 4.0.49
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 +10 -10
- package/dist/ElvClient-node-min.js +5 -5
- package/dist/ElvFrameClient-min.js +5 -5
- package/dist/ElvPermissionsClient-min.js +9 -9
- package/dist/ElvWalletClient-min.js +4 -4
- package/dist/ElvWalletClient-node-min.js +8 -8
- package/dist/src/FrameClient.js +1 -1
- package/dist/src/Validation.js +1 -1
- package/dist/src/client/ContentAccess.js +405 -574
- package/dist/src/client/ContentManagement.js +1 -1
- package/package.json +1 -1
- package/src/ElvClient.js +81 -0
- package/src/FrameClient.js +6 -0
- package/src/HttpClient.js +12 -1
- package/src/UserProfileClient.js +13 -0
- package/src/Utils.js +11 -0
- package/src/client/LiveConf.js +351 -0
- package/src/client/LiveStream.js +1546 -0
|
@@ -2215,7 +2215,7 @@ exports.UpdateContentObjectGraph = /*#__PURE__*/function () {
|
|
|
2215
2215
|
return _context30.delegateYield(_loop(), "t0", 8);
|
|
2216
2216
|
case 8:
|
|
2217
2217
|
_ret = _context30.t0;
|
|
2218
|
-
if (!_ret) {
|
|
2218
|
+
if (!(_typeof(_ret) === "object")) {
|
|
2219
2219
|
_context30.next = 11;
|
|
2220
2220
|
break;
|
|
2221
2221
|
}
|
package/package.json
CHANGED
package/src/ElvClient.js
CHANGED
|
@@ -20,6 +20,7 @@ const Pako = require("pako");
|
|
|
20
20
|
const {
|
|
21
21
|
ValidatePresence
|
|
22
22
|
} = require("./Validation");
|
|
23
|
+
const CBOR = require("cbor");
|
|
23
24
|
|
|
24
25
|
const networks = {
|
|
25
26
|
"main": "https://main.net955305.contentfabric.io",
|
|
@@ -565,6 +566,85 @@ class ElvClient {
|
|
|
565
566
|
}
|
|
566
567
|
}
|
|
567
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Return a list of nodes in the content space, optionally filtered by node ID or endpoint.
|
|
571
|
+
*
|
|
572
|
+
* @methodGroup Nodes
|
|
573
|
+
* @namedParams
|
|
574
|
+
* @param {string=} matchEndpoint - Return node(s) matching the specified endpoint
|
|
575
|
+
* @param {string=} matchNodeId - Return node(s) matching the specified node ID
|
|
576
|
+
*
|
|
577
|
+
* @return {Array<Object>} - A list of nodes in the space matching the parameters
|
|
578
|
+
*/
|
|
579
|
+
async SpaceNodes({matchEndpoint, matchNodeId}={}) {
|
|
580
|
+
let bign = await this.CallContractMethod({
|
|
581
|
+
contractAddress: this.contentSpaceAddress,
|
|
582
|
+
methodName: "numActiveNodes",
|
|
583
|
+
});
|
|
584
|
+
let n = bign.toNumber();
|
|
585
|
+
|
|
586
|
+
return (await Utils.LimitedMap(
|
|
587
|
+
5,
|
|
588
|
+
[...new Array(n)],
|
|
589
|
+
async (_, index) => {
|
|
590
|
+
let bigi = Ethers.BigNumber.from(index);
|
|
591
|
+
let addr = await this.CallContractMethod({
|
|
592
|
+
contractAddress: this.contentSpaceAddress,
|
|
593
|
+
methodName: "activeNodeAddresses",
|
|
594
|
+
methodArgs: [bigi],
|
|
595
|
+
formatArguments: true
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
let locatorsHex = await this.CallContractMethod({
|
|
599
|
+
contractAddress: this.contentSpaceAddress,
|
|
600
|
+
methodName: "activeNodeLocators",
|
|
601
|
+
methodArgs: [bigi]
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
let nodeId = this.utils.AddressToNodeId(addr);
|
|
605
|
+
|
|
606
|
+
if(matchNodeId &&nodeId !== matchNodeId) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
let node = {id: nodeId, endpoints: []};
|
|
611
|
+
|
|
612
|
+
// Parse locators CBOR
|
|
613
|
+
let buffer = locatorsHex.slice(2, locatorsHex.length); // Skip "0x"
|
|
614
|
+
let hex = buffer.toString("hex");
|
|
615
|
+
let locators = CBOR.decodeAllSync(hex);
|
|
616
|
+
|
|
617
|
+
let match = false;
|
|
618
|
+
|
|
619
|
+
if(locators.length >= 5) {
|
|
620
|
+
let fabArray = locators[4].fab;
|
|
621
|
+
if(fabArray) {
|
|
622
|
+
for(let i = 0; i < fabArray.length; i ++) {
|
|
623
|
+
let host = fabArray[i].host;
|
|
624
|
+
|
|
625
|
+
if(matchEndpoint && !matchEndpoint.includes(host)) {
|
|
626
|
+
continue; // Not a match
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
match = true;
|
|
630
|
+
let endpoint = fabArray[i].scheme + "://" + host;
|
|
631
|
+
|
|
632
|
+
if(fabArray[i].port) {
|
|
633
|
+
endpoint = endpoint + ":" + fabArray[i].port;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
endpoint = endpoint + "/" + fabArray[i].path;
|
|
637
|
+
node.endpoints.push(endpoint);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return match ? node : undefined;
|
|
643
|
+
}
|
|
644
|
+
))
|
|
645
|
+
.filter(n => n);
|
|
646
|
+
}
|
|
647
|
+
|
|
568
648
|
/**
|
|
569
649
|
* Return information about how the client was connected to the network
|
|
570
650
|
*
|
|
@@ -1201,6 +1281,7 @@ Object.assign(ElvClient.prototype, require("./client/ContentAccess"));
|
|
|
1201
1281
|
Object.assign(ElvClient.prototype, require("./client/Contracts"));
|
|
1202
1282
|
Object.assign(ElvClient.prototype, require("./client/Files"));
|
|
1203
1283
|
Object.assign(ElvClient.prototype, require("./client/ABRPublishing"));
|
|
1284
|
+
Object.assign(ElvClient.prototype, require("./client/LiveStream"));
|
|
1204
1285
|
Object.assign(ElvClient.prototype, require("./client/ContentManagement"));
|
|
1205
1286
|
Object.assign(ElvClient.prototype, require("./client/NTP"));
|
|
1206
1287
|
Object.assign(ElvClient.prototype, require("./client/NFT"));
|
package/src/FrameClient.js
CHANGED
|
@@ -470,7 +470,12 @@ class FrameClient {
|
|
|
470
470
|
"SetStaticToken",
|
|
471
471
|
"SetVisibility",
|
|
472
472
|
"SetPermission",
|
|
473
|
+
"SpaceNodes",
|
|
473
474
|
"StartABRMezzanineJobs",
|
|
475
|
+
"StreamConfig",
|
|
476
|
+
"StreamCreate",
|
|
477
|
+
"StreamStatus",
|
|
478
|
+
"StreamStartOrStopOrReset",
|
|
474
479
|
"SuspendNTPInstance",
|
|
475
480
|
"UnlinkAccessGroupFromOauth",
|
|
476
481
|
"UpdateContentObjectGraph",
|
|
@@ -496,6 +501,7 @@ class FrameClient {
|
|
|
496
501
|
"MergeUserMetadata",
|
|
497
502
|
"PublicUserMetadata",
|
|
498
503
|
"ReplaceUserMetadata",
|
|
504
|
+
"TenantContractId",
|
|
499
505
|
"TenantId",
|
|
500
506
|
"UserMetadata",
|
|
501
507
|
"UserProfileImage",
|
package/src/HttpClient.js
CHANGED
|
@@ -171,8 +171,19 @@ class HttpClient {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
URL({path, queryParams={}}) {
|
|
174
|
+
let baseURI = this.BaseURI();
|
|
175
|
+
|
|
176
|
+
// If URL contains a write token, it must go to the correct server and can not fail over
|
|
177
|
+
const writeTokenMatch = path.replace(/^\//, "").match(/(qlibs\/ilib[a-zA-Z0-9]+|q|qid)\/(tqw__[a-zA-Z0-9]+)/);
|
|
178
|
+
const writeToken = writeTokenMatch ? writeTokenMatch[2] : undefined;
|
|
179
|
+
|
|
180
|
+
if(writeToken && this.draftURIs[writeToken]) {
|
|
181
|
+
// Use saved write token URI
|
|
182
|
+
baseURI = this.draftURIs[writeToken];
|
|
183
|
+
}
|
|
184
|
+
|
|
174
185
|
return (
|
|
175
|
-
|
|
186
|
+
baseURI
|
|
176
187
|
.path(path)
|
|
177
188
|
.query(queryParams)
|
|
178
189
|
.hash("")
|
package/src/UserProfileClient.js
CHANGED
|
@@ -469,6 +469,19 @@ await client.userProfileClient.UserMetadata()
|
|
|
469
469
|
this.tenantId = id;
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Return the ID of the tenant contract this user belongs to, if set.
|
|
474
|
+
*
|
|
475
|
+
* @return {Promise<string>} - Tenant Contract ID
|
|
476
|
+
*/
|
|
477
|
+
async TenantContractId() {
|
|
478
|
+
if(!this.tenantContractId) {
|
|
479
|
+
this.tenantContractId = await this.UserMetadata({metadataSubtree: "tenantContractId"});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return this.tenantContractId;
|
|
483
|
+
}
|
|
484
|
+
|
|
472
485
|
/**
|
|
473
486
|
* Get the URL of the current user's profile image
|
|
474
487
|
*
|
package/src/Utils.js
CHANGED
|
@@ -315,6 +315,17 @@ const Utils = {
|
|
|
315
315
|
return "ispc" + Utils.AddressToHash(address);
|
|
316
316
|
},
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Convert contract address to node ID
|
|
320
|
+
*
|
|
321
|
+
* @param {string} address - Address of contract
|
|
322
|
+
*
|
|
323
|
+
* @returns {string} - Node ID from contract address
|
|
324
|
+
*/
|
|
325
|
+
AddressToNodeId: (address) => {
|
|
326
|
+
return "inod" + Utils.AddressToHash(address);
|
|
327
|
+
},
|
|
328
|
+
|
|
318
329
|
/**
|
|
319
330
|
* Convert contract address to content library ID
|
|
320
331
|
*
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
const LadderTemplate = {
|
|
2
|
+
"2160": {
|
|
3
|
+
bit_rate: 14000000,
|
|
4
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
5
|
+
height: 2160,
|
|
6
|
+
media_type: 1,
|
|
7
|
+
representation: "videovideo_3840x2160_h264@14000000",
|
|
8
|
+
stream_name: "video",
|
|
9
|
+
width: 3840
|
|
10
|
+
},
|
|
11
|
+
"1080": {
|
|
12
|
+
bit_rate: 9500000,
|
|
13
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
14
|
+
height: 1080,
|
|
15
|
+
media_type: 1,
|
|
16
|
+
representation: "videovideo_1920x1080_h264@9500000",
|
|
17
|
+
stream_name: "video",
|
|
18
|
+
width: 1920
|
|
19
|
+
},
|
|
20
|
+
"720": {
|
|
21
|
+
bit_rate: 4500000,
|
|
22
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
23
|
+
height: 720,
|
|
24
|
+
media_type: 1,
|
|
25
|
+
representation: "videovideo_1280x720_h264@4500000",
|
|
26
|
+
stream_name: "video",
|
|
27
|
+
width: 1280
|
|
28
|
+
},
|
|
29
|
+
"540": {
|
|
30
|
+
bit_rate: 2000000,
|
|
31
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
32
|
+
height: 540,
|
|
33
|
+
media_type: 1,
|
|
34
|
+
representation: "videovideo_960x540_h264@2000000",
|
|
35
|
+
stream_name: "video",
|
|
36
|
+
width: 960
|
|
37
|
+
},
|
|
38
|
+
"360": {
|
|
39
|
+
bit_rate: 520000,
|
|
40
|
+
codecs: "avc1.640028,mp4a.40.2",
|
|
41
|
+
height: 360,
|
|
42
|
+
media_type: 1,
|
|
43
|
+
representation: "videovideo_640x360_h264@520000",
|
|
44
|
+
stream_name: "video",
|
|
45
|
+
width: 640
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const LiveconfTemplate = {
|
|
50
|
+
live_recording: {
|
|
51
|
+
fabric_config: {
|
|
52
|
+
ingress_node_api: "",
|
|
53
|
+
ingress_node_id: ""
|
|
54
|
+
},
|
|
55
|
+
playout_config: {
|
|
56
|
+
rebroadcast_start_time_sec_epoch: 0,
|
|
57
|
+
vod_enabled: false
|
|
58
|
+
},
|
|
59
|
+
recording_config: {
|
|
60
|
+
recording_params: {
|
|
61
|
+
description: "",
|
|
62
|
+
ladder_specs: [
|
|
63
|
+
{
|
|
64
|
+
bit_rate: 384000,
|
|
65
|
+
channels: 2,
|
|
66
|
+
codecs: "mp4a.40.2",
|
|
67
|
+
media_type: 2,
|
|
68
|
+
representation: "audioaudio_aac@384000",
|
|
69
|
+
stream_name: "audio"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
listen: true,
|
|
73
|
+
live_delay_nano: 2000000000,
|
|
74
|
+
max_duration_sec: -1,
|
|
75
|
+
name: "",
|
|
76
|
+
origin_url: "",
|
|
77
|
+
part_ttl: 3600,
|
|
78
|
+
playout_type: "live",
|
|
79
|
+
source_timescale: null,
|
|
80
|
+
xc_params: {
|
|
81
|
+
audio_bitrate: 384000,
|
|
82
|
+
audio_index: [
|
|
83
|
+
0,
|
|
84
|
+
0,
|
|
85
|
+
0,
|
|
86
|
+
0,
|
|
87
|
+
0,
|
|
88
|
+
0,
|
|
89
|
+
0,
|
|
90
|
+
0
|
|
91
|
+
],
|
|
92
|
+
audio_seg_duration_ts: null,
|
|
93
|
+
ecodec2: "aac",
|
|
94
|
+
enc_height: null,
|
|
95
|
+
enc_width: null,
|
|
96
|
+
filter_descriptor: "",
|
|
97
|
+
force_keyint: null,
|
|
98
|
+
format: "fmp4-segment",
|
|
99
|
+
listen: true,
|
|
100
|
+
n_audio: 1,
|
|
101
|
+
preset: "faster",
|
|
102
|
+
sample_rate: 48000,
|
|
103
|
+
seg_duration: null,
|
|
104
|
+
skip_decoding: false,
|
|
105
|
+
start_segment_str: "1",
|
|
106
|
+
stream_id: -1,
|
|
107
|
+
sync_audio_to_stream_id: -1,
|
|
108
|
+
video_bitrate: null,
|
|
109
|
+
video_seg_duration_ts: null,
|
|
110
|
+
xc_type: 3
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
class LiveConf {
|
|
118
|
+
constructor(probeData, nodeId, nodeUrl, includeAVSegDurations, overwriteOriginUrl, syncAudioToVideo) {
|
|
119
|
+
this.probeData = probeData;
|
|
120
|
+
this.nodeId = nodeId;
|
|
121
|
+
this.nodeUrl = nodeUrl;
|
|
122
|
+
this.includeAVSegDurations = includeAVSegDurations;
|
|
123
|
+
this.overwriteOriginUrl = overwriteOriginUrl;
|
|
124
|
+
this.syncAudioToVideo = syncAudioToVideo;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
probeKind() {
|
|
128
|
+
let fileNameSplit = this.probeData.format.filename.split(":");
|
|
129
|
+
return fileNameSplit[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getStreamDataForCodecType(codecType) {
|
|
133
|
+
let stream = null;
|
|
134
|
+
for(let index = 0; index < this.probeData.streams.length; index++) {
|
|
135
|
+
if(this.probeData.streams[index].codec_type == codecType) {
|
|
136
|
+
stream = this.probeData.streams[index];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return stream;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getFrameRate() {
|
|
143
|
+
let videoStream = this.getStreamDataForCodecType("video");
|
|
144
|
+
let frameRate = videoStream.r_frame_rate || videoStream.frame_rate;
|
|
145
|
+
return frameRate.split("/");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
isFrameRateWhole() {
|
|
149
|
+
let frameRate = this.getFrameRate();
|
|
150
|
+
return frameRate[1] == "1";
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
getForceKeyint() {
|
|
154
|
+
let frameRate = this.getFrameRate();
|
|
155
|
+
let roundedFrameRate = Math.round(frameRate[0] / frameRate[1]);
|
|
156
|
+
if(roundedFrameRate > 30) {
|
|
157
|
+
return roundedFrameRate;
|
|
158
|
+
} else {
|
|
159
|
+
return roundedFrameRate * 2;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
calcSegDuration({sourceTimescale}) {
|
|
164
|
+
|
|
165
|
+
let videoStream = this.getStreamDataForCodecType("video");
|
|
166
|
+
let frameRate = videoStream.frame_rate;
|
|
167
|
+
|
|
168
|
+
let seg ={};
|
|
169
|
+
switch(frameRate) {
|
|
170
|
+
case "24":
|
|
171
|
+
seg.video = 30 * sourceTimescale;
|
|
172
|
+
seg.audio = 30 * 48000;
|
|
173
|
+
seg.keyint = 48;
|
|
174
|
+
seg.duration = "30";
|
|
175
|
+
break;
|
|
176
|
+
case "25":
|
|
177
|
+
seg.video = 30 * sourceTimescale;
|
|
178
|
+
seg.audio = 30 * 48000;
|
|
179
|
+
seg.keyint = 50;
|
|
180
|
+
seg.duration = "30";
|
|
181
|
+
break;
|
|
182
|
+
case "30":
|
|
183
|
+
seg.video = 30 * sourceTimescale;
|
|
184
|
+
seg.audio = 30 * 48000;
|
|
185
|
+
seg.keyint = 60;
|
|
186
|
+
seg.duration = "30";
|
|
187
|
+
break;
|
|
188
|
+
case "30000/1001":
|
|
189
|
+
seg.video = 30.03 * sourceTimescale;
|
|
190
|
+
seg.audio = 29.76 * 48000;
|
|
191
|
+
seg.keyint = 60;
|
|
192
|
+
seg.duration = "30.03";
|
|
193
|
+
break;
|
|
194
|
+
case "48":
|
|
195
|
+
seg.video = 30 * sourceTimescale;
|
|
196
|
+
seg.audio = 30 * 48000;
|
|
197
|
+
seg.keyint = 96;
|
|
198
|
+
seg.duration = "30";
|
|
199
|
+
break;
|
|
200
|
+
case "50":
|
|
201
|
+
seg.video = 30 * sourceTimescale;
|
|
202
|
+
seg.audio = 30 * 48000;
|
|
203
|
+
seg.keyint = 100;
|
|
204
|
+
seg.duration = "30";
|
|
205
|
+
break;
|
|
206
|
+
case "60":
|
|
207
|
+
seg.video = 30 * sourceTimescale;
|
|
208
|
+
seg.audio = 30 * 48000;
|
|
209
|
+
seg.keyint = 120;
|
|
210
|
+
seg.duration = "30";
|
|
211
|
+
break;
|
|
212
|
+
case "60000/1001":
|
|
213
|
+
seg.video = 30.03 * sourceTimescale;
|
|
214
|
+
seg.audio = 29.76 * 48000;
|
|
215
|
+
seg.keyint = 120;
|
|
216
|
+
seg.duration = "30.03";
|
|
217
|
+
break;
|
|
218
|
+
default:
|
|
219
|
+
console.log("Unsupported frame rate", frameRate);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
return seg;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
syncAudioToStreamIdValue() {
|
|
226
|
+
let sync_id = -1;
|
|
227
|
+
let videoStream = this.getStreamDataForCodecType("video");
|
|
228
|
+
switch(this.probeKind()) {
|
|
229
|
+
case "udp":
|
|
230
|
+
sync_id = videoStream.stream_id;
|
|
231
|
+
break;
|
|
232
|
+
case "rtmp":
|
|
233
|
+
sync_id = -1; // Pending fabric API: videoStream.stream_index
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
return sync_id;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
generateLiveConf() {
|
|
240
|
+
// gather required data
|
|
241
|
+
const conf = LiveconfTemplate;
|
|
242
|
+
const fileName = this.overwriteOriginUrl || this.probeData.format.filename;
|
|
243
|
+
const audioStream = this.getStreamDataForCodecType("audio");
|
|
244
|
+
const sampleRate = parseInt(audioStream.sample_rate);
|
|
245
|
+
const videoStream = this.getStreamDataForCodecType("video");
|
|
246
|
+
let sourceTimescale;
|
|
247
|
+
|
|
248
|
+
console.log("AUDIO", audioStream);
|
|
249
|
+
console.log("VIDEO", videoStream);
|
|
250
|
+
|
|
251
|
+
// Fill in liveconf all formats have in common
|
|
252
|
+
conf.live_recording.fabric_config.ingress_node_api = this.nodeUrl || null;
|
|
253
|
+
conf.live_recording.fabric_config.ingress_node_id = this.nodeId || null;
|
|
254
|
+
conf.live_recording.recording_config.recording_params.description;
|
|
255
|
+
conf.live_recording.recording_config.recording_params.origin_url = fileName;
|
|
256
|
+
conf.live_recording.recording_config.recording_params.description = `Ingest stream ${fileName}`;
|
|
257
|
+
conf.live_recording.recording_config.recording_params.name = `Ingest stream ${fileName}`;
|
|
258
|
+
conf.live_recording.recording_config.recording_params.xc_params.audio_index[0] = audioStream.stream_index;
|
|
259
|
+
conf.live_recording.recording_config.recording_params.xc_params.sample_rate = sampleRate;
|
|
260
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
|
|
261
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
|
|
262
|
+
|
|
263
|
+
if(this.syncAudioToVideo) {
|
|
264
|
+
conf.live_recording.recording_config.recording_params.xc_params.sync_audio_to_stream_id = this.syncAudioToStreamIdValue();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Fill in specifics for protocol
|
|
268
|
+
switch(this.probeKind()) {
|
|
269
|
+
case "udp":
|
|
270
|
+
sourceTimescale = 90000;
|
|
271
|
+
conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
|
|
272
|
+
break;
|
|
273
|
+
case "rtmp":
|
|
274
|
+
sourceTimescale = 16000;
|
|
275
|
+
conf.live_recording.recording_config.recording_params.source_timescale = sourceTimescale;
|
|
276
|
+
break;
|
|
277
|
+
case "hls":
|
|
278
|
+
console.log("HLS detected. Not yet implemented");
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
console.log("Unsuppoted media", this.probeKind());
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const segDurations = this.calcSegDuration({sourceTimescale});
|
|
286
|
+
|
|
287
|
+
// Segment conditioning parameters
|
|
288
|
+
conf.live_recording.recording_config.recording_params.xc_params.seg_duration = segDurations.duration;
|
|
289
|
+
conf.live_recording.recording_config.recording_params.xc_params.audio_seg_duration_ts = segDurations.audio;
|
|
290
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_seg_duration_ts = segDurations.video;
|
|
291
|
+
conf.live_recording.recording_config.recording_params.xc_params.force_keyint = segDurations.keyint;
|
|
292
|
+
|
|
293
|
+
switch(videoStream.height) {
|
|
294
|
+
case 2160:
|
|
295
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
|
|
296
|
+
LadderTemplate[2160],
|
|
297
|
+
LadderTemplate[1080],
|
|
298
|
+
LadderTemplate[720],
|
|
299
|
+
LadderTemplate[540],
|
|
300
|
+
LadderTemplate[360]
|
|
301
|
+
);
|
|
302
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[2160].bit_rate;
|
|
303
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = 2160;
|
|
304
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = 3840;
|
|
305
|
+
|
|
306
|
+
break;
|
|
307
|
+
case 1080:
|
|
308
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
|
|
309
|
+
LadderTemplate[1080],
|
|
310
|
+
LadderTemplate[720],
|
|
311
|
+
LadderTemplate[540],
|
|
312
|
+
LadderTemplate[360]
|
|
313
|
+
);
|
|
314
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[1080].bit_rate;
|
|
315
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = 1080;
|
|
316
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = 1920;
|
|
317
|
+
break;
|
|
318
|
+
case 720:
|
|
319
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
|
|
320
|
+
LadderTemplate[720],
|
|
321
|
+
LadderTemplate[540],
|
|
322
|
+
LadderTemplate[360]
|
|
323
|
+
);
|
|
324
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[720].bit_rate;
|
|
325
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = 720;
|
|
326
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = 1280;
|
|
327
|
+
break;
|
|
328
|
+
case 540:
|
|
329
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(
|
|
330
|
+
LadderTemplate[540],
|
|
331
|
+
LadderTemplate[360]
|
|
332
|
+
);
|
|
333
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[540].bit_rate;
|
|
334
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = 540;
|
|
335
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = 960;
|
|
336
|
+
break;
|
|
337
|
+
case 360:
|
|
338
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(LadderTemplate[360]);
|
|
339
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.unshift(LadderTemplate[360]);
|
|
340
|
+
conf.live_recording.recording_config.recording_params.xc_params.video_bitrate = LadderTemplate[360].bit_rate;
|
|
341
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_height = 360;
|
|
342
|
+
conf.live_recording.recording_config.recording_params.xc_params.enc_width = 640;
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
throw new Error("ERROR: Probed stream does not conform to one of the following built in resolution ladders [4096, 2160], [1920, 1080] [1280, 720], [960, 540], [640, 360]");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return JSON.stringify(conf, null, 2);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
exports.LiveConf = LiveConf;
|