@eluvio/elv-client-js 4.0.83 → 4.0.85
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 +6 -6
- package/dist/ElvClient-node-min.js +6 -6
- package/dist/ElvFrameClient-min.js +1 -1
- package/dist/ElvWalletClient-min.js +3 -3
- package/dist/ElvWalletClient-node-min.js +3 -3
- package/dist/src/client/ContentAccess.js +2 -2
- package/dist/src/client/ContentManagement.js +33 -12
- package/dist/src/client/LiveConf.js +118 -31
- package/dist/src/client/LiveStream.js +411 -467
- package/package.json +1 -1
- package/src/client/ContentAccess.js +2 -2
- package/src/client/ContentManagement.js +21 -6
- package/src/client/LiveConf.js +113 -28
- package/src/client/LiveStream.js +120 -145
package/package.json
CHANGED
|
@@ -1152,8 +1152,8 @@ exports.LatestVersionHashV2 = async function({objectId, versionHash}) {
|
|
|
1152
1152
|
latestHash = q.hash;
|
|
1153
1153
|
|
|
1154
1154
|
} catch(error) {
|
|
1155
|
-
|
|
1156
|
-
throw
|
|
1155
|
+
error.message = `Unable to determine latest version hash for ${versionHash || objectId}`;
|
|
1156
|
+
throw error;
|
|
1157
1157
|
}
|
|
1158
1158
|
return latestHash;
|
|
1159
1159
|
};
|
|
@@ -1054,13 +1054,28 @@ exports.PublishContentVersion = async function({objectId, versionHash, awaitComm
|
|
|
1054
1054
|
if(awaitCommitConfirmation) {
|
|
1055
1055
|
const pollingInterval = 500; // ms
|
|
1056
1056
|
let tries = 20;
|
|
1057
|
-
while
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1057
|
+
while(tries > 0) {
|
|
1058
|
+
let h;
|
|
1059
|
+
|
|
1060
|
+
try {
|
|
1061
|
+
h = await this.LatestVersionHashV2({objectId});
|
|
1062
|
+
|
|
1063
|
+
if(h === versionHash) {
|
|
1064
|
+
this.Log(`Commit confirmed on fabric node: ${versionHash}`);
|
|
1065
|
+
break;
|
|
1066
|
+
} else {
|
|
1067
|
+
tries--;
|
|
1068
|
+
await new Promise(resolve => setTimeout(resolve, pollingInterval));
|
|
1069
|
+
}
|
|
1070
|
+
} catch(error) {
|
|
1071
|
+
console.error(error);
|
|
1072
|
+
if(error.status !== 404) {
|
|
1073
|
+
throw error;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
tries--;
|
|
1077
|
+
await new Promise(resolve => setTimeout(resolve, pollingInterval));
|
|
1062
1078
|
}
|
|
1063
|
-
await new Promise(resolve => setTimeout(resolve, pollingInterval));
|
|
1064
1079
|
}
|
|
1065
1080
|
}
|
|
1066
1081
|
|
package/src/client/LiveConf.js
CHANGED
|
@@ -15,6 +15,7 @@ const LadderTemplate = {
|
|
|
15
15
|
media_type: 1,
|
|
16
16
|
representation: "videovideo_1920x1080_h264@9500000",
|
|
17
17
|
stream_name: "video",
|
|
18
|
+
stream_index: 0,
|
|
18
19
|
width: 1920
|
|
19
20
|
},
|
|
20
21
|
"720": {
|
|
@@ -24,6 +25,7 @@ const LadderTemplate = {
|
|
|
24
25
|
media_type: 1,
|
|
25
26
|
representation: "videovideo_1280x720_h264@4500000",
|
|
26
27
|
stream_name: "video",
|
|
28
|
+
stream_index: 0,
|
|
27
29
|
width: 1280
|
|
28
30
|
},
|
|
29
31
|
"540": {
|
|
@@ -33,6 +35,7 @@ const LadderTemplate = {
|
|
|
33
35
|
media_type: 1,
|
|
34
36
|
representation: "videovideo_960x540_h264@2000000",
|
|
35
37
|
stream_name: "video",
|
|
38
|
+
stream_index: 0,
|
|
36
39
|
width: 960
|
|
37
40
|
},
|
|
38
41
|
"540_low": {
|
|
@@ -42,6 +45,7 @@ const LadderTemplate = {
|
|
|
42
45
|
media_type: 1,
|
|
43
46
|
representation: "videovideo_960x540_h264@900000",
|
|
44
47
|
stream_name: "video",
|
|
48
|
+
stream_index: 0,
|
|
45
49
|
width: 960
|
|
46
50
|
}
|
|
47
51
|
};
|
|
@@ -59,16 +63,7 @@ const LiveconfTemplate = {
|
|
|
59
63
|
recording_config: {
|
|
60
64
|
recording_params: {
|
|
61
65
|
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
|
-
],
|
|
66
|
+
ladder_specs: [],
|
|
72
67
|
listen: true,
|
|
73
68
|
live_delay_nano: 2000000000,
|
|
74
69
|
max_duration_sec: -1,
|
|
@@ -90,6 +85,7 @@ const LiveconfTemplate = {
|
|
|
90
85
|
0
|
|
91
86
|
],
|
|
92
87
|
audio_seg_duration_ts: null,
|
|
88
|
+
connection_timeout: 60,
|
|
93
89
|
ecodec2: "aac",
|
|
94
90
|
enc_height: null,
|
|
95
91
|
enc_width: null,
|
|
@@ -108,6 +104,7 @@ const LiveconfTemplate = {
|
|
|
108
104
|
video_bitrate: null,
|
|
109
105
|
video_seg_duration_ts: null,
|
|
110
106
|
video_time_base: null,
|
|
107
|
+
video_frame_duration_ts: null,
|
|
111
108
|
xc_type: 3
|
|
112
109
|
}
|
|
113
110
|
}
|
|
@@ -115,6 +112,16 @@ const LiveconfTemplate = {
|
|
|
115
112
|
}
|
|
116
113
|
};
|
|
117
114
|
|
|
115
|
+
const LadderSpecAudio = {
|
|
116
|
+
bit_rate: 384000,
|
|
117
|
+
channels: 2,
|
|
118
|
+
codecs: "mp4a.40.2",
|
|
119
|
+
media_type: 2,
|
|
120
|
+
representation: "audioaudio_aac@384000",
|
|
121
|
+
stream_name: "audio",
|
|
122
|
+
stream_index: 0
|
|
123
|
+
};
|
|
124
|
+
|
|
118
125
|
class LiveConf {
|
|
119
126
|
constructor(probeData, nodeId, nodeUrl, includeAVSegDurations, overwriteOriginUrl, syncAudioToVideo) {
|
|
120
127
|
this.probeData = probeData;
|
|
@@ -140,6 +147,22 @@ class LiveConf {
|
|
|
140
147
|
return stream;
|
|
141
148
|
}
|
|
142
149
|
|
|
150
|
+
// Return all audio streams found in the probe
|
|
151
|
+
// Used by generateAudioStreamsConfig()
|
|
152
|
+
getAudioStreamsFromProbe() {
|
|
153
|
+
let audioStreams = {};
|
|
154
|
+
for(let index = 0; index < this.probeData.streams.length; index++) {
|
|
155
|
+
if(this.probeData.streams[index].codec_type == "audio") {
|
|
156
|
+
audioStreams[index] = {
|
|
157
|
+
recordingBitrate: Math.max(this.probeData.streams[index].bit_rate, 128000),
|
|
158
|
+
recordingChannels: this.probeData.streams[index].channels,
|
|
159
|
+
playoutLabel: `Audio ${index}`
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return audioStreams;
|
|
164
|
+
}
|
|
165
|
+
|
|
143
166
|
getFrameRate() {
|
|
144
167
|
let videoStream = this.getStreamDataForCodecType("video");
|
|
145
168
|
let frameRate = videoStream.r_frame_rate || videoStream.frame_rate;
|
|
@@ -223,7 +246,12 @@ class LiveConf {
|
|
|
223
246
|
calcSegDurationMpegts({sourceTimescale}) {
|
|
224
247
|
let videoStream = this.getStreamDataForCodecType("video");
|
|
225
248
|
let frameRate = videoStream.frame_rate;
|
|
226
|
-
|
|
249
|
+
|
|
250
|
+
// PENDING(SS) - calculate frame duration here
|
|
251
|
+
// let frameRateNum = 0;
|
|
252
|
+
let seg = {
|
|
253
|
+
// videoFrameDurationTs: sourceTimescale / frameRateNum
|
|
254
|
+
};
|
|
227
255
|
|
|
228
256
|
switch(frameRate) {
|
|
229
257
|
case "24":
|
|
@@ -242,21 +270,25 @@ class LiveConf {
|
|
|
242
270
|
seg.duration = "30";
|
|
243
271
|
break;
|
|
244
272
|
case "30000/1001":
|
|
273
|
+
//seg.videoFrameDurationTs = 3003;
|
|
245
274
|
seg.video = sourceTimescale * 30;
|
|
246
275
|
seg.keyint = 60;
|
|
247
276
|
seg.duration = "30.03";
|
|
248
277
|
break;
|
|
249
278
|
case "48":
|
|
279
|
+
//seg.videoFrameDurationTs = 1875;
|
|
250
280
|
seg.video = sourceTimescale * 30;
|
|
251
281
|
seg.keyint = 96;
|
|
252
282
|
seg.duration = "30";
|
|
253
283
|
break;
|
|
254
284
|
case "50":
|
|
285
|
+
//seg.videoFrameDurationTs = 1800;
|
|
255
286
|
seg.video = sourceTimescale * 30;
|
|
256
287
|
seg.keyint = 100;
|
|
257
288
|
seg.duration = "30";
|
|
258
289
|
break;
|
|
259
290
|
case "60":
|
|
291
|
+
//seg.videoFrameDurationTs = 1500;
|
|
260
292
|
seg.video = sourceTimescale * 30;
|
|
261
293
|
seg.keyint = 120;
|
|
262
294
|
seg.duration = "30";
|
|
@@ -281,7 +313,7 @@ class LiveConf {
|
|
|
281
313
|
|
|
282
314
|
switch(frameRate) {
|
|
283
315
|
case "24":
|
|
284
|
-
seg.videoTimeBase =
|
|
316
|
+
seg.videoTimeBase = 768; // Note 1536 produces low output bitrate
|
|
285
317
|
seg.videoFrameDurationTs = 512;
|
|
286
318
|
seg.video = this.calcOutputTimebase(seg.videoTimeBase) * 30;
|
|
287
319
|
seg.keyint = 48;
|
|
@@ -350,36 +382,77 @@ class LiveConf {
|
|
|
350
382
|
return sync_id;
|
|
351
383
|
}
|
|
352
384
|
|
|
353
|
-
|
|
385
|
+
/*
|
|
386
|
+
* Generate audio streams recording configuration based on the optional custom settings.
|
|
387
|
+
* If no custom "audio" section is present, record all the acceptable audio streams found in the probe
|
|
388
|
+
*/
|
|
389
|
+
generateAudioStreamsConfig({customSettings}) {
|
|
390
|
+
|
|
391
|
+
let audioStreams = {};
|
|
392
|
+
if (customSettings && customSettings.audio) {
|
|
393
|
+
for (let i = 0; i < Object.keys(customSettings.audio).length; i ++) {
|
|
394
|
+
let audioIdx = Object.keys(customSettings.audio)[i];
|
|
395
|
+
let audio = customSettings.audio[audioIdx];
|
|
396
|
+
audioStreams[audioIdx] = {
|
|
397
|
+
recordingBitrate: audio.recording_bitrate || 192000,
|
|
398
|
+
recordingChannels: audio.recording_channels || 2,
|
|
399
|
+
};
|
|
400
|
+
if (audio.playout) {
|
|
401
|
+
audioStreams[audioIdx].playoutLabel = audio.playout_label || `Audio ${audioIdx}`
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// If no audio streams specified in custom config, set up all the suitable audio streams in the probe
|
|
407
|
+
if (!customSettings.audio) {
|
|
408
|
+
audioStreams = this.getAudioStreamsFromProbe();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return audioStreams;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/*
|
|
415
|
+
* Generate the live recording config as required by QFAB, based on defaults and optional custom settings.
|
|
416
|
+
*/
|
|
417
|
+
generateLiveConf({customSettings}) {
|
|
354
418
|
// gather required data
|
|
355
419
|
const conf = JSON.parse(JSON.stringify(LiveconfTemplate));
|
|
356
420
|
const fileName = this.overwriteOriginUrl || this.probeData.format.filename;
|
|
357
|
-
const
|
|
421
|
+
const audioStreams = this.generateAudioStreamsConfig({customSettings});
|
|
358
422
|
|
|
423
|
+
// Retrieve one audio stream from the probe to read the sample rate and codec name
|
|
424
|
+
const audioStream = this.getStreamDataForCodecType("audio");
|
|
359
425
|
const sampleRate = parseInt(audioStream.sample_rate);
|
|
360
426
|
const audioCodec = audioStream.codec_name;
|
|
427
|
+
|
|
361
428
|
const videoStream = this.getStreamDataForCodecType("video");
|
|
362
429
|
let sourceTimescale;
|
|
363
430
|
|
|
364
431
|
// Fill in liveconf all formats have in common
|
|
365
|
-
conf.live_recording.probe_info = this.probeData;
|
|
366
432
|
conf.live_recording.fabric_config.ingress_node_api = this.nodeUrl || null;
|
|
367
433
|
conf.live_recording.fabric_config.ingress_node_id = this.nodeId || null;
|
|
368
434
|
conf.live_recording.recording_config.recording_params.description;
|
|
369
435
|
conf.live_recording.recording_config.recording_params.origin_url = fileName;
|
|
370
436
|
conf.live_recording.recording_config.recording_params.description = `Ingest stream ${fileName}`;
|
|
371
437
|
conf.live_recording.recording_config.recording_params.name = `Ingest stream ${fileName}`;
|
|
372
|
-
conf.live_recording.recording_config.recording_params.xc_params.audio_index[0] = audioIndex === undefined ? audioStream.stream_index : audioIndex;
|
|
373
438
|
conf.live_recording.recording_config.recording_params.xc_params.sample_rate = sampleRate;
|
|
374
439
|
conf.live_recording.recording_config.recording_params.xc_params.enc_height = videoStream.height;
|
|
375
440
|
conf.live_recording.recording_config.recording_params.xc_params.enc_width = videoStream.width;
|
|
376
441
|
|
|
442
|
+
for (let i =0; i < Object.keys(audioStreams).length; i ++) {
|
|
443
|
+
conf.live_recording.recording_config.recording_params.xc_params.audio_index[i] = parseInt(Object.keys(audioStreams)[i]);
|
|
444
|
+
}
|
|
445
|
+
|
|
377
446
|
if(this.syncAudioToVideo) {
|
|
378
447
|
conf.live_recording.recording_config.recording_params.xc_params.sync_audio_to_stream_id = this.syncAudioToStreamIdValue();
|
|
379
448
|
}
|
|
380
449
|
|
|
381
|
-
if(
|
|
382
|
-
conf.live_recording.
|
|
450
|
+
if(customSettings.edge_write_token) {
|
|
451
|
+
conf.live_recording.fabric_config.edge_write_token = customSettings.edge_write_token;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if(customSettings.part_ttl) {
|
|
455
|
+
conf.live_recording.recording_config.recording_params.part_ttl = customSettings.part_ttl;
|
|
383
456
|
}
|
|
384
457
|
|
|
385
458
|
// Fill in specifics for protocol
|
|
@@ -472,21 +545,33 @@ class LiveConf {
|
|
|
472
545
|
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]");
|
|
473
546
|
}
|
|
474
547
|
|
|
475
|
-
if(audioBitrate || channelLayout) {
|
|
476
|
-
const audioLadderSpec = conf.live_recording.recording_config.recording_params.ladder_specs.find(spec => spec.stream_name === "audio");
|
|
477
548
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
audioLadderSpec.bit_rate = audioBitrate;
|
|
481
|
-
audioLadderSpec.representation = `audioaudio_aac@${audioBitrate}`;
|
|
482
|
-
}
|
|
549
|
+
let globalAudioBitrate = 0;
|
|
550
|
+
let nAudio = 0;
|
|
483
551
|
|
|
484
|
-
|
|
485
|
-
|
|
552
|
+
for (let i = 0; i < Object.keys(audioStreams).length; i ++ ) {
|
|
553
|
+
let audioLadderSpec = {...LadderSpecAudio};
|
|
554
|
+
const audioIndex = Object.keys(audioStreams)[i];
|
|
555
|
+
const audio = audioStreams[audioIndex];
|
|
556
|
+
audioLadderSpec.bit_rate = audio.recordingBitrate;
|
|
557
|
+
audioLadderSpec.representation = `audioaudio_aac@${audio.recordingBitrate}`;
|
|
558
|
+
audioLadderSpec.channels = audio.recordingChannels;
|
|
559
|
+
audioLadderSpec.stream_index = parseInt(audioIndex);
|
|
560
|
+
audioLadderSpec.stream_name = `audio_${audioIndex}`;
|
|
561
|
+
audioLadderSpec.stream_label = audio.playoutLabel ? audio.playoutLabel : null;
|
|
562
|
+
|
|
563
|
+
conf.live_recording.recording_config.recording_params.ladder_specs.push(audioLadderSpec);
|
|
564
|
+
if (audio.recordingBitrate > globalAudioBitrate) {
|
|
565
|
+
globalAudioBitrate = audio.recordingBitrate;
|
|
486
566
|
}
|
|
567
|
+
nAudio ++;
|
|
487
568
|
}
|
|
488
569
|
|
|
489
|
-
|
|
570
|
+
// Global recording bitrate for all audio streams
|
|
571
|
+
conf.live_recording.recording_config.recording_params.xc_params.audio_bitrate = globalAudioBitrate;
|
|
572
|
+
conf.live_recording.recording_config.recording_params.xc_params.n_audio = nAudio;
|
|
573
|
+
|
|
574
|
+
return conf;
|
|
490
575
|
}
|
|
491
576
|
}
|
|
492
577
|
exports.LiveConf = LiveConf;
|