@eluvio/elv-client-js 4.0.46 → 4.0.48
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 +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/Validation.js +7 -1
- package/src/client/ContentAccess.js +11 -2
- package/src/client/LiveConf.js +351 -0
- package/src/client/LiveStream.js +1546 -0
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
|
|
575
|
+
* @param {string} - matchNodeId
|
|
576
|
+
*
|
|
577
|
+
* @return {Array<Object>} - A list of nodes in the space
|
|
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
|
+
let nodes = [];
|
|
586
|
+
|
|
587
|
+
for (let i = 0; i < n; i ++) {
|
|
588
|
+
|
|
589
|
+
let bigi = Ethers.BigNumber.from(i);
|
|
590
|
+
let addr = await this.CallContractMethod({
|
|
591
|
+
contractAddress: this.contentSpaceAddress,
|
|
592
|
+
methodName: "activeNodeAddresses",
|
|
593
|
+
methodArgs: [bigi],
|
|
594
|
+
formatArguments: true
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
let locatorsHex = await this.CallContractMethod({
|
|
598
|
+
contractAddress: this.contentSpaceAddress,
|
|
599
|
+
methodName: "activeNodeLocators",
|
|
600
|
+
methodArgs: [bigi]
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
let nodeId = this.utils.AddressToNodeId(addr);
|
|
604
|
+
|
|
605
|
+
if (matchNodeId != undefined && nodeId != matchNodeId) {
|
|
606
|
+
continue; // Not a match
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
let node = {id: nodeId, endpoints: []};
|
|
610
|
+
|
|
611
|
+
// Parse locators CBOR
|
|
612
|
+
let buffer = locatorsHex.slice(2, locatorsHex.length); // Skip "0x"
|
|
613
|
+
let hex = buffer.toString("hex");
|
|
614
|
+
let locators = CBOR.decodeAllSync(hex);
|
|
615
|
+
|
|
616
|
+
let match = false;
|
|
617
|
+
|
|
618
|
+
if(locators.length >= 5) {
|
|
619
|
+
let fabArray = locators[4].fab;
|
|
620
|
+
if(fabArray != undefined) {
|
|
621
|
+
for(let i = 0; i < fabArray.length; i ++) {
|
|
622
|
+
let host = fabArray[i].host;
|
|
623
|
+
|
|
624
|
+
if(matchEndpoint != undefined && !host.includes(matchEndpoint)) {
|
|
625
|
+
continue; // Not a match
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
match = true;
|
|
629
|
+
let endpoint = fabArray[i].scheme + "://" + host;
|
|
630
|
+
|
|
631
|
+
if(fabArray[i].port != "") {
|
|
632
|
+
endpoint = endpoint + ":" + fabArray[i].port;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
endpoint = endpoint + "/" + fabArray[i].path;
|
|
636
|
+
node.endpoints.push(endpoint);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if(match) {
|
|
641
|
+
nodes.push(node);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return nodes;
|
|
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
|
*
|
package/src/Validation.js
CHANGED
|
@@ -41,7 +41,13 @@ exports.ValidateWriteToken = (writeToken) => {
|
|
|
41
41
|
exports.ValidatePartHash = (partHash) => {
|
|
42
42
|
if(!partHash) {
|
|
43
43
|
throw Error("Part hash not specified");
|
|
44
|
-
}
|
|
44
|
+
}
|
|
45
|
+
else if(!partHash.toString().startsWith("hqp_") &&
|
|
46
|
+
!partHash.toString().startsWith("hqpe") &&
|
|
47
|
+
!partHash.toString().startsWith("hqt_") &&
|
|
48
|
+
!partHash.toString().startsWith("hqte") &&
|
|
49
|
+
!partHash.toString().startsWith("hql_") &&
|
|
50
|
+
!partHash.toString().startsWith("hqle")) {
|
|
45
51
|
throw Error(`Invalid part hash: ${partHash}`);
|
|
46
52
|
}
|
|
47
53
|
};
|
|
@@ -2144,7 +2144,7 @@ exports.ContentObjectImageUrl = async function({libraryId, objectId, versionHash
|
|
|
2144
2144
|
* capLevelToPlayerSize - Caps video quality to player size
|
|
2145
2145
|
* clipEnd - End time for the video
|
|
2146
2146
|
* clipStart - Start time for the video
|
|
2147
|
-
* controls - Sets the player control visibility. Values:
|
|
2147
|
+
* controls - Sets the player control visibility. Values: browserDefault | autoHide | show | hide | hideWithVolume. Defaults to autoHide
|
|
2148
2148
|
* description - Sets the page description
|
|
2149
2149
|
* directLink - If enabled, sets direct link
|
|
2150
2150
|
* linkPath - Video link path
|
|
@@ -2175,6 +2175,13 @@ exports.EmbedUrl = async function({
|
|
|
2175
2175
|
// Default options
|
|
2176
2176
|
options.controls = options.controls === undefined ? "autoHide" : options.controls;
|
|
2177
2177
|
|
|
2178
|
+
const controlsMap = {
|
|
2179
|
+
autoHide: "h",
|
|
2180
|
+
browserDefault: "d",
|
|
2181
|
+
show: "s",
|
|
2182
|
+
hideWithVolume: "hv"
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2178
2185
|
let embedUrl = new URL("https://embed.v3.contentfabric.io");
|
|
2179
2186
|
const networkInfo = await this.NetworkInfo();
|
|
2180
2187
|
const networkName = networkInfo.name === "demov3" ? "demo" : (networkInfo.name === "test" && networkInfo.id === 955205) ? "testv4" : networkInfo.name;
|
|
@@ -2207,7 +2214,9 @@ exports.EmbedUrl = async function({
|
|
|
2207
2214
|
embedUrl.searchParams.set("start", options.clipStart);
|
|
2208
2215
|
break;
|
|
2209
2216
|
case "controls":
|
|
2210
|
-
|
|
2217
|
+
if(options.controls !== "hide") {
|
|
2218
|
+
embedUrl.searchParams.set("ct", controlsMap[options.controls]);
|
|
2219
|
+
}
|
|
2211
2220
|
break;
|
|
2212
2221
|
case "description":
|
|
2213
2222
|
data["og:description"] = options.description;
|
|
@@ -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;
|