@norskvideo/norsk-sdk 1.0.343 → 1.0.344
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/norsk-sdk.d.ts +105 -26
- package/lib/src/media_nodes/common.d.ts +9 -5
- package/lib/src/media_nodes/common.js +67 -27
- package/lib/src/media_nodes/input.d.ts +35 -0
- package/lib/src/media_nodes/input.js +73 -1
- package/lib/src/media_nodes/output.d.ts +2 -2
- package/lib/src/media_nodes/output.js +46 -18
- package/lib/src/media_nodes/processor.d.ts +26 -8
- package/lib/src/media_nodes/processor.js +121 -42
- package/lib/src/media_nodes/types.d.ts +6 -1
- package/lib/src/media_nodes/types.js +164 -3
- package/lib/src/sdk.d.ts +10 -2
- package/lib/src/sdk.js +27 -4
- package/package.json +2 -2
- package/src/sdk.ts +31 -4
package/dist/norsk-sdk.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ import { CmafAudioMessage } from '@norskvideo/norsk-api/lib/media_pb';
|
|
|
5
5
|
import { CmafMultiVariantMessage } from '@norskvideo/norsk-api/lib/media_pb';
|
|
6
6
|
import { CmafVideoMessage } from '@norskvideo/norsk-api/lib/media_pb';
|
|
7
7
|
import { CmafWebVttMessage } from '@norskvideo/norsk-api/lib/media_pb';
|
|
8
|
-
import { Context } from '@norskvideo/norsk-api/lib/media_pb';
|
|
9
8
|
import { CurrentLoad } from '@norskvideo/norsk-api/lib/shared/common_pb';
|
|
10
9
|
import { ExplicitChannel } from '@norskvideo/norsk-api/lib/media_pb';
|
|
11
10
|
import { FileTsInputMessage } from '@norskvideo/norsk-api/lib/media_pb';
|
|
11
|
+
import { FrameRate as FrameRate_2 } from '@norskvideo/norsk-api/lib/media_pb';
|
|
12
12
|
import { GopStructure } from '@norskvideo/norsk-api/lib/media_pb';
|
|
13
13
|
import * as grpc from '@grpc/grpc-js';
|
|
14
14
|
import { HlsOutputEvent } from '@norskvideo/norsk-api/lib/media_pb';
|
|
@@ -412,7 +412,7 @@ export declare interface AutoProcessorMediaNode<Pins extends string> extends Sou
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
export declare class AutoProcessorMediaNode<Pins extends string> {
|
|
415
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, getGrpcStream: () => (Readable | Writable), subscribeFn: (subscription: Subscription) =>
|
|
415
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, getGrpcStream: () => (Readable | Writable), subscribeFn: (subscription: Subscription) => Promise<boolean>, subscribeErrorFn?: (error: SubscriptionError) => void, subscribedStreamsChangedFn?: (streams: StreamMetadata[]) => void);
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
/** @public */
|
|
@@ -774,12 +774,12 @@ export declare interface CmafMultiVariantOutputSettings extends SinkNodeSettings
|
|
|
774
774
|
}
|
|
775
775
|
|
|
776
776
|
declare class CmafNodeBase<ClientMessage, Pins extends string, T extends MediaNodeState> extends AutoProcessorMediaNode<Pins> {
|
|
777
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) =>
|
|
777
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) => Promise<boolean>, _playlistPath: PlaylistPath, subscribedStreamsChangedFn?: (streams: StreamMetadata[]) => void);
|
|
778
778
|
close(): void;
|
|
779
779
|
}
|
|
780
780
|
|
|
781
781
|
declare class CmafNodeWithPlaylist<ClientMessage, Pins extends string, T extends MediaNodeState> extends CmafNodeBase<ClientMessage, Pins, T> {
|
|
782
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) =>
|
|
782
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) => Promise<boolean>, playlistPath: PlaylistPath, sessionId: string);
|
|
783
783
|
/**
|
|
784
784
|
* @public
|
|
785
785
|
* Returns the URL to the HLS playlist entry. Note this can only be evaluated once the stream is active as it
|
|
@@ -984,6 +984,11 @@ export declare interface ComposePart<Pins> {
|
|
|
984
984
|
referenceResolution?: Resolution;
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
+
/** @public */
|
|
988
|
+
export declare interface Context {
|
|
989
|
+
streams: StreamMetadata[];
|
|
990
|
+
}
|
|
991
|
+
|
|
987
992
|
/** @public */
|
|
988
993
|
export declare type ContextType = "full" | "singleSource" | "singleProgram" | "singleStream" | "singleRendition";
|
|
989
994
|
|
|
@@ -1065,6 +1070,9 @@ export declare type DeckLinkVideoConnection = "sdi" | "hdmi" | "optical_sdi" | "
|
|
|
1065
1070
|
/** @public */
|
|
1066
1071
|
export declare type DeckLinkVideoIOSupport = "capture" | "playback";
|
|
1067
1072
|
|
|
1073
|
+
/** @public */
|
|
1074
|
+
export declare type DeferredVideoComposeSettings<Pins extends string> = (streams: StreamMetadata[]) => VideoComposeSettings<Pins> | undefined;
|
|
1075
|
+
|
|
1068
1076
|
/**
|
|
1069
1077
|
* @public
|
|
1070
1078
|
* Drop every N frames from an incoming video stream
|
|
@@ -1939,6 +1947,11 @@ export declare interface NorskInput {
|
|
|
1939
1947
|
* @param settings - Configuration for the RTP input
|
|
1940
1948
|
*/
|
|
1941
1949
|
rtp(settings: RtpInputSettings): Promise<RtpInputNode>;
|
|
1950
|
+
/**
|
|
1951
|
+
* Generate a test video card with a configurable pattern.
|
|
1952
|
+
* @param settings - Configuration for the video test card
|
|
1953
|
+
*/
|
|
1954
|
+
videoTestCard(settings: VideoTestcardGeneratorSettings): Promise<VideoTestcardGeneratorNode>;
|
|
1942
1955
|
/**
|
|
1943
1956
|
* Generate a test audio signal with a configurable waveform.
|
|
1944
1957
|
* @param settings - Configuration for the audio signal
|
|
@@ -2183,7 +2196,7 @@ export declare interface NorskTransform {
|
|
|
2183
2196
|
* Compose multiple video streams together into a single output
|
|
2184
2197
|
* @param settings - Composition settings
|
|
2185
2198
|
*/
|
|
2186
|
-
videoCompose<Pins extends string>(settings: VideoComposeSettings<Pins>): Promise<VideoComposeNode<Pins>>;
|
|
2199
|
+
videoCompose<Pins extends string>(settings: VideoComposeSettings<Pins> | DeferredVideoComposeSettings<Pins>): Promise<VideoComposeNode<Pins>>;
|
|
2187
2200
|
/**
|
|
2188
2201
|
* Create a Media Node performing transcription into subtitles using the
|
|
2189
2202
|
* Amazon Transcribe AWS service.
|
|
@@ -2283,7 +2296,7 @@ export declare interface NorskTransform {
|
|
|
2283
2296
|
* of the time taken to set up connections and subscriptions across various protocols, are off by a few
|
|
2284
2297
|
* hundred milliseconds
|
|
2285
2298
|
*/
|
|
2286
|
-
|
|
2299
|
+
streamAlign(settings: StreamAlignSettings): Promise<StreamAlignNode>;
|
|
2287
2300
|
ancillary(settings: AncillarySettings): Promise<AncillaryNode>;
|
|
2288
2301
|
}
|
|
2289
2302
|
|
|
@@ -2504,6 +2517,8 @@ export declare interface PartTransition {
|
|
|
2504
2517
|
easing?: SimpleEasing;
|
|
2505
2518
|
}
|
|
2506
2519
|
|
|
2520
|
+
declare type Pattern = "black" | "smpte75" | "smpte100";
|
|
2521
|
+
|
|
2507
2522
|
/** @public */
|
|
2508
2523
|
export declare type PinToKey<Pins extends string> = Nullable<Partial<Record<Pins, StreamKey[]>>>;
|
|
2509
2524
|
|
|
@@ -2545,7 +2560,7 @@ export declare interface ProcessorMediaNode<Pins extends string> extends SourceM
|
|
|
2545
2560
|
}
|
|
2546
2561
|
|
|
2547
2562
|
export declare class ProcessorMediaNode<Pins extends string> {
|
|
2548
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, getGrpcStream: () => (Readable | Writable), subscribeFn: (subscription: Subscription) =>
|
|
2563
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, getGrpcStream: () => (Readable | Writable), subscribeFn: (subscription: Subscription) => Promise<boolean>, subscribeErrorFn?: (error: SubscriptionError) => void, subscribedStreamsChangedFn?: (streams: StreamMetadata[]) => void);
|
|
2549
2564
|
}
|
|
2550
2565
|
|
|
2551
2566
|
/** @public */
|
|
@@ -2963,6 +2978,9 @@ export declare function selectAudio(streams: readonly StreamMetadata[]): StreamK
|
|
|
2963
2978
|
*/
|
|
2964
2979
|
export declare function selectAV(streams: readonly StreamMetadata[]): StreamKey[];
|
|
2965
2980
|
|
|
2981
|
+
/** @public */
|
|
2982
|
+
export declare function selectExactKey(key: StreamKey): (streams: readonly StreamMetadata[]) => StreamKey[];
|
|
2983
|
+
|
|
2966
2984
|
/** @public */
|
|
2967
2985
|
export declare function selectPlaylist(streams: readonly StreamMetadata[]): StreamKey[];
|
|
2968
2986
|
|
|
@@ -2988,7 +3006,7 @@ export declare interface SingleStreamStatistics extends StreamStatistics {
|
|
|
2988
3006
|
}
|
|
2989
3007
|
|
|
2990
3008
|
/** @public */
|
|
2991
|
-
export declare class SinkMediaNode<Pins extends string> extends MediaNodeState {
|
|
3009
|
+
export declare class SinkMediaNode<Pins extends string> extends MediaNodeState implements SubscribeDestination {
|
|
2992
3010
|
permissiveSubscriptionValidation(_context: Context): SubscriptionValidationResponse;
|
|
2993
3011
|
restrictiveSubscriptionValidation(context: Context): SubscriptionValidationResponse;
|
|
2994
3012
|
/** Subscribe to the given sources.
|
|
@@ -3028,8 +3046,8 @@ export declare interface Smpte2038Message {
|
|
|
3028
3046
|
/** @public */
|
|
3029
3047
|
export declare class SourceMediaNode extends MediaNodeState {
|
|
3030
3048
|
outputStreams: StreamMetadata[];
|
|
3031
|
-
registerForContextChange(subscriber:
|
|
3032
|
-
unregisterForContextChange(subscriber:
|
|
3049
|
+
registerForContextChange(subscriber: SubscribeDestination): void;
|
|
3050
|
+
unregisterForContextChange(subscriber: SubscribeDestination): void;
|
|
3033
3051
|
}
|
|
3034
3052
|
|
|
3035
3053
|
/** @public */
|
|
@@ -3075,6 +3093,9 @@ export declare type SourceSubscriptionError = {
|
|
|
3075
3093
|
reason: "unsupportedConversion";
|
|
3076
3094
|
};
|
|
3077
3095
|
|
|
3096
|
+
/** @public */
|
|
3097
|
+
export declare function sourceToPin<Pins extends string>(source: string, pin: Pins): (streams: StreamMetadata[]) => PinToKey<Pins>;
|
|
3098
|
+
|
|
3078
3099
|
/**
|
|
3079
3100
|
* @public
|
|
3080
3101
|
* The return value for the {@link SrtInputSettings.onConnection} callback
|
|
@@ -3212,6 +3233,26 @@ export declare interface SrtOutputSettings extends SinkNodeSettings<SrtOutputNod
|
|
|
3212
3233
|
/** @public */
|
|
3213
3234
|
export declare type StabilizationMode = "low" | "medium" | "high";
|
|
3214
3235
|
|
|
3236
|
+
/**
|
|
3237
|
+
* @public
|
|
3238
|
+
* see: {@link NorskTransform.streamSync}
|
|
3239
|
+
*/
|
|
3240
|
+
export declare class StreamAlignNode extends AutoProcessorMediaNode<"audio" | "video"> {
|
|
3241
|
+
close(): void;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
/**
|
|
3245
|
+
* @public
|
|
3246
|
+
* Settings for a StreamAlign node
|
|
3247
|
+
* This will reset all streams to the same framerates/sample rates
|
|
3248
|
+
* and align their timestamps so that they completely line up for downstream operations
|
|
3249
|
+
* see {@link NorskTransform.streamAlign}
|
|
3250
|
+
* */
|
|
3251
|
+
export declare interface StreamAlignSettings extends ProcessorNodeSettings<StreamAlignNode> {
|
|
3252
|
+
sampleRate: SampleRate;
|
|
3253
|
+
frameRate: FrameRate;
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3215
3256
|
/**
|
|
3216
3257
|
* @public
|
|
3217
3258
|
* see: {@link NorskTransform.streamChaosMonkey}
|
|
@@ -3262,6 +3303,11 @@ export declare interface StreamKeyOverrideSettings extends ProcessorNodeSettings
|
|
|
3262
3303
|
streamKey: StreamKey;
|
|
3263
3304
|
}
|
|
3264
3305
|
|
|
3306
|
+
/**
|
|
3307
|
+
* Compares two stream keys by value, returning true if the stream keys refer to the same stream
|
|
3308
|
+
*/
|
|
3309
|
+
export declare function streamKeysAreEqual(l: StreamKey, r: StreamKey): unknown;
|
|
3310
|
+
|
|
3265
3311
|
/** @public */
|
|
3266
3312
|
export declare interface StreamKeySettings {
|
|
3267
3313
|
/** Source name. Default: the rtmp app */
|
|
@@ -3341,22 +3387,6 @@ export declare interface StreamMetadataOverrideSettingsUpdate {
|
|
|
3341
3387
|
};
|
|
3342
3388
|
}
|
|
3343
3389
|
|
|
3344
|
-
/**
|
|
3345
|
-
* @public
|
|
3346
|
-
* see: {@link NorskTransform.streamSync}
|
|
3347
|
-
*/
|
|
3348
|
-
export declare class StreamProgramJoinNode extends AutoProcessorMediaNode<"audio" | "video"> {
|
|
3349
|
-
close(): void;
|
|
3350
|
-
}
|
|
3351
|
-
|
|
3352
|
-
/**
|
|
3353
|
-
* @public
|
|
3354
|
-
* Settings for a StreamProgramJoin node
|
|
3355
|
-
* see {@link NorskTransform.streamSync}
|
|
3356
|
-
* */
|
|
3357
|
-
export declare interface StreamProgramJoinSettings extends ProcessorNodeSettings<StreamProgramJoinNode> {
|
|
3358
|
-
}
|
|
3359
|
-
|
|
3360
3390
|
/** @public */
|
|
3361
3391
|
export declare interface StreamStatistics {
|
|
3362
3392
|
/** The size of the sample window in seconds */
|
|
@@ -3463,8 +3493,20 @@ export declare interface StreamSwitchSmoothSettings<Pins extends string> extends
|
|
|
3463
3493
|
transitionDurationMs?: number;
|
|
3464
3494
|
/** The constant resolution that all output video will be scaled to */
|
|
3465
3495
|
outputResolution: Resolution;
|
|
3496
|
+
/** The constant framerate that all output video will be sampled to */
|
|
3497
|
+
frameRate: FrameRate;
|
|
3466
3498
|
/** The constant samplerate that all output audio will be resampled to */
|
|
3467
3499
|
sampleRate: SampleRate;
|
|
3500
|
+
/** The constant channel layout that all output audio will be resampled to */
|
|
3501
|
+
channelLayout: ChannelLayout;
|
|
3502
|
+
/** Alignment behaviour of the component
|
|
3503
|
+
* whether to rebase all incoming streams to a common timeline
|
|
3504
|
+
* Note: This will modify the timestamps, meaning that merging streams not involved in this may result in
|
|
3505
|
+
* operation may result in sync issues. To avoid this, you can use {@link NorskProcessor.streamAlign} instead of relying
|
|
3506
|
+
* on this component for this behaviour
|
|
3507
|
+
* Note: This behaviour may be removed in a future release and replaced with something similar
|
|
3508
|
+
* */
|
|
3509
|
+
alignment?: "aligned" | "not_aligned";
|
|
3468
3510
|
/** Callback which will be called if a switch request cannot be fulfilled */
|
|
3469
3511
|
onSwitchError?: (message: string, inputPin?: Pins) => void;
|
|
3470
3512
|
/** Callback which will be called a transition has succesfully completed for a requested switch, i.e. the new source
|
|
@@ -3521,6 +3563,11 @@ export declare interface StreamTimestampNudgeSettings extends ProcessorNodeSetti
|
|
|
3521
3563
|
nudge?: number;
|
|
3522
3564
|
}
|
|
3523
3565
|
|
|
3566
|
+
export declare interface SubscribeDestination {
|
|
3567
|
+
id?: string;
|
|
3568
|
+
sourceContextChange(responseCallback: (error?: SubscriptionError) => void): Promise<boolean>;
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3524
3571
|
/**
|
|
3525
3572
|
* @public
|
|
3526
3573
|
* Errors found while setting up subscriptions, separated out by reason:
|
|
@@ -3825,6 +3872,7 @@ export declare interface VideoStreamMetadata {
|
|
|
3825
3872
|
codec: string;
|
|
3826
3873
|
width: number;
|
|
3827
3874
|
height: number;
|
|
3875
|
+
frameRate?: FrameRate_2;
|
|
3828
3876
|
}
|
|
3829
3877
|
|
|
3830
3878
|
/**
|
|
@@ -3835,6 +3883,37 @@ export declare interface VideoStreamMetadata {
|
|
|
3835
3883
|
*/
|
|
3836
3884
|
export declare function videoStreams(streams: readonly StreamMetadata[]): StreamMetadata[];
|
|
3837
3885
|
|
|
3886
|
+
/**
|
|
3887
|
+
* @public
|
|
3888
|
+
* see: {@link NorskInput.audioSignal}
|
|
3889
|
+
*/
|
|
3890
|
+
export declare class VideoTestcardGeneratorNode extends SourceMediaNode {
|
|
3891
|
+
close(): void;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
/**
|
|
3895
|
+
* @public
|
|
3896
|
+
* Settings for an Video Testcard Generator
|
|
3897
|
+
* see: {@link NorskInput.videoTestcard}
|
|
3898
|
+
* */
|
|
3899
|
+
export declare interface VideoTestcardGeneratorSettings extends SourceNodeSettings<VideoTestcardGeneratorNode> {
|
|
3900
|
+
/** The source name to set in the stream key of the outgoing stream */
|
|
3901
|
+
sourceName: string;
|
|
3902
|
+
/** The number of frames to send before shutting down */
|
|
3903
|
+
numberOfFrames?: number;
|
|
3904
|
+
/** Resolution of the test card stream **/
|
|
3905
|
+
resolution: {
|
|
3906
|
+
width: number;
|
|
3907
|
+
height: number;
|
|
3908
|
+
};
|
|
3909
|
+
/** Framerate of the produced video stream **/
|
|
3910
|
+
frameRate: {
|
|
3911
|
+
frames: number;
|
|
3912
|
+
seconds: number;
|
|
3913
|
+
};
|
|
3914
|
+
pattern: Pattern;
|
|
3915
|
+
}
|
|
3916
|
+
|
|
3838
3917
|
/** @public */
|
|
3839
3918
|
export declare function videoToPin<Pins extends string>(pin: Pins): (streams: StreamMetadata[]) => PinToKey<Pins>;
|
|
3840
3919
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MediaClient } from "@norskvideo/norsk-api/lib/media_grpc_pb";
|
|
2
|
-
import {
|
|
2
|
+
import { StreamStatisticsSampling, GopStructure } from "@norskvideo/norsk-api/lib/media_pb";
|
|
3
3
|
import { Nullable } from "typescript-nullable";
|
|
4
|
-
import { StreamKey, MediaNodeId, StreamMetadata, MultiStreamStatistics, SubscriptionError } from "./types";
|
|
4
|
+
import { StreamKey, MediaNodeId, StreamMetadata, Context, MultiStreamStatistics, SubscriptionError } from "./types";
|
|
5
5
|
export * from "../shared/utils";
|
|
6
6
|
import { PlainMessage } from "@bufbuild/protobuf";
|
|
7
7
|
/**
|
|
@@ -74,11 +74,15 @@ export declare type ReceiveFromAddressAuto = {
|
|
|
74
74
|
};
|
|
75
75
|
/** @public */
|
|
76
76
|
export declare type PinToKey<Pins extends string> = Nullable<Partial<Record<Pins, StreamKey[]>>>;
|
|
77
|
+
export interface SubscribeDestination {
|
|
78
|
+
id?: string;
|
|
79
|
+
sourceContextChange(responseCallback: (error?: SubscriptionError) => void): Promise<boolean>;
|
|
80
|
+
}
|
|
77
81
|
/** @public */
|
|
78
82
|
export declare class SourceMediaNode extends MediaNodeState {
|
|
79
83
|
outputStreams: StreamMetadata[];
|
|
80
|
-
registerForContextChange(subscriber:
|
|
81
|
-
unregisterForContextChange(subscriber:
|
|
84
|
+
registerForContextChange(subscriber: SubscribeDestination): void;
|
|
85
|
+
unregisterForContextChange(subscriber: SubscribeDestination): void;
|
|
82
86
|
}
|
|
83
87
|
/**
|
|
84
88
|
* @public
|
|
@@ -91,7 +95,7 @@ export declare class SourceMediaNode extends MediaNodeState {
|
|
|
91
95
|
* */
|
|
92
96
|
export declare type SubscriptionValidationResponse = true | false | "accept" | "deny" | "accept_and_terminate";
|
|
93
97
|
/** @public */
|
|
94
|
-
export declare class SinkMediaNode<Pins extends string> extends MediaNodeState {
|
|
98
|
+
export declare class SinkMediaNode<Pins extends string> extends MediaNodeState implements SubscribeDestination {
|
|
95
99
|
permissiveSubscriptionValidation(_context: Context): SubscriptionValidationResponse;
|
|
96
100
|
restrictiveSubscriptionValidation(context: Context): SubscriptionValidationResponse;
|
|
97
101
|
/** Subscribe to the given sources.
|
|
@@ -44,6 +44,7 @@ const types_1 = require("./types");
|
|
|
44
44
|
__exportStar(require("../shared/utils"), exports);
|
|
45
45
|
const utils_1 = require("../shared/utils");
|
|
46
46
|
const crypto_1 = require("crypto");
|
|
47
|
+
const sdk_1 = require("../sdk");
|
|
47
48
|
/** @internal */
|
|
48
49
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
50
|
function applyMixins(derivedCtor, baseCtors) {
|
|
@@ -182,24 +183,24 @@ class SinkMediaNode extends MediaNodeState {
|
|
|
182
183
|
const numStreams = context.streams.length;
|
|
183
184
|
const expected = this.currentSubscription.sources.length;
|
|
184
185
|
if (numStreams == 0 && this.subscriptionValidated) {
|
|
185
|
-
(0, utils_1.debuglog)("Node '%s' accepting inbound context because it is empty and we have previously accepted a full context %O", this.id,
|
|
186
|
+
(0, utils_1.debuglog)("Node '%s' accepting inbound context because it is empty and we have previously accepted a full context %O", this.id, debugFormatSdkContext(context));
|
|
186
187
|
this.subscriptionValidated = false;
|
|
187
188
|
return "accept";
|
|
188
189
|
}
|
|
189
190
|
if (this.currentSourcesActive < this.currentSources.size && !this.subscriptionValidated) {
|
|
190
|
-
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because not all sources are active yet %O", this.id,
|
|
191
|
+
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because not all sources are active yet %O", this.id, debugFormatSdkContext(context));
|
|
191
192
|
return "deny";
|
|
192
193
|
}
|
|
193
194
|
if (this.currentSourcesActive < this.currentSources.size) {
|
|
194
|
-
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because it is smaller than the already-accepted one %O", this.id,
|
|
195
|
+
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because it is smaller than the already-accepted one %O", this.id, debugFormatSdkContext(context));
|
|
195
196
|
return "deny";
|
|
196
197
|
}
|
|
197
198
|
if (numStreams == expected) {
|
|
198
|
-
(0, utils_1.debuglog)("Node '%s' accepting inbound context because all keys and streams are accounted for %O", this.id,
|
|
199
|
+
(0, utils_1.debuglog)("Node '%s' accepting inbound context because all keys and streams are accounted for %O", this.id, debugFormatSdkContext(context));
|
|
199
200
|
this.subscriptionValidated = true;
|
|
200
201
|
return "accept";
|
|
201
202
|
}
|
|
202
|
-
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because not all keys are accounted for yet %O", this.id,
|
|
203
|
+
(0, utils_1.debuglog)("Node '%s' ignoring inbound context because not all keys are accounted for yet %O", this.id, debugFormatSdkContext(context));
|
|
203
204
|
return "deny";
|
|
204
205
|
}
|
|
205
206
|
/** Subscribe to the given sources.
|
|
@@ -267,7 +268,7 @@ class SinkMediaNode extends MediaNodeState {
|
|
|
267
268
|
this.currentSourcesActive += 1;
|
|
268
269
|
for (const pin in pinSubscriptions) {
|
|
269
270
|
(_a = pinSubscriptions[pin]) === null || _a === void 0 ? void 0 : _a.forEach((key) => {
|
|
270
|
-
this.subscribedStreams.push(source.outputStreams.filter((streamMetadata) => streamMetadata.streamKey
|
|
271
|
+
this.subscribedStreams.push(source.outputStreams.filter((streamMetadata) => streamMetadata.streamKey && (0, sdk_1.streamKeysAreEqual)(streamMetadata.streamKey, key))[0]);
|
|
271
272
|
subscriberSources.push((0, utils_1.provideFull)(media_pb_1.SubscribeSource, {
|
|
272
273
|
stream: (0, types_1.mkStreamKey)(key),
|
|
273
274
|
nodeId: (0, types_1.toMediaNodeId)(source.id),
|
|
@@ -284,39 +285,48 @@ class SinkMediaNode extends MediaNodeState {
|
|
|
284
285
|
const id = (0, crypto_1.randomUUID)();
|
|
285
286
|
this.currentSubscription = (0, utils_1.provideFull)(media_pb_1.Subscription, { sources, id });
|
|
286
287
|
(0, utils_1.debuglog)("Node '%s' sending subscriptions %O", this.id, debugFormatSubscription(this.currentSubscription));
|
|
287
|
-
this.subscribeFn(this.currentSubscription);
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
let subscribed = yield this.subscribeFn(this.currentSubscription);
|
|
289
|
+
if (subscribed)
|
|
290
|
+
this.subscriptionResponseCallbacks.set(id, responseCallback);
|
|
291
|
+
return subscribed;
|
|
290
292
|
});
|
|
291
293
|
}
|
|
292
294
|
/** @internal */
|
|
293
295
|
subscriptionResponse(response) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
297
|
+
yield this.finaliseSubscription(response.id, response.error);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/** @internal */
|
|
301
|
+
finaliseSubscription(id, err) {
|
|
302
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
303
|
+
const callback = this.subscriptionResponseCallbacks.get(id);
|
|
304
|
+
this.subscriptionResponseCallbacks.delete(id);
|
|
305
|
+
if (callback) {
|
|
306
|
+
if (err === undefined) {
|
|
307
|
+
callback();
|
|
308
|
+
if (this.subscribedStreamsChangedFn) {
|
|
309
|
+
this.subscribedStreamsChangedFn(this.subscribedStreams);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
const error = (0, types_1.fromSubscriptionError)(err);
|
|
314
|
+
(0, utils_1.debuglog)("Node '%s' has a failed subscription response for '%s': %O", this.id, id, error);
|
|
315
|
+
callback(error);
|
|
316
|
+
if (this.subscribeErrorFn) {
|
|
317
|
+
this.subscribeErrorFn(error);
|
|
318
|
+
}
|
|
301
319
|
}
|
|
302
320
|
}
|
|
303
321
|
else {
|
|
304
|
-
|
|
305
|
-
(0, utils_1.debuglog)("Node '%s' has a failed subscription response for '%s': %O", this.id, response.id, error);
|
|
306
|
-
callback(error);
|
|
307
|
-
if (this.subscribeErrorFn) {
|
|
308
|
-
this.subscribeErrorFn(error);
|
|
309
|
-
}
|
|
322
|
+
(0, utils_1.debuglog)("Missing subscription response callback for '%s'", id);
|
|
310
323
|
}
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
(0, utils_1.debuglog)("Missing subscription response callback for '%s'", response.id);
|
|
314
|
-
}
|
|
324
|
+
});
|
|
315
325
|
}
|
|
316
326
|
/** @internal */
|
|
317
327
|
handleInboundContext(context) {
|
|
318
328
|
return __awaiter(this, void 0, void 0, function* () {
|
|
319
|
-
const validationResponse = this.subscriptionValidation(context);
|
|
329
|
+
const validationResponse = this.subscriptionValidation((0, types_1.fromContext)(context));
|
|
320
330
|
const responseFn = util
|
|
321
331
|
.promisify(this.client.sendValidationResponse)
|
|
322
332
|
.bind(this.client);
|
|
@@ -428,6 +438,36 @@ function registerStreamHandlers(grpcStream, unregisterNode, tag, reject, setting
|
|
|
428
438
|
exports.registerStreamHandlers = registerStreamHandlers;
|
|
429
439
|
////////////////////////////////////////////////////////////////////////////////
|
|
430
440
|
// debug
|
|
441
|
+
function debugFormatSdkContext(context) {
|
|
442
|
+
return {
|
|
443
|
+
keys: context.streams.map((stream) => debugFormatSdkStreamMetadata(stream))
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function debugFormatSdkStreamMetadata(stream) {
|
|
447
|
+
switch (stream.message.case) {
|
|
448
|
+
case "audio": {
|
|
449
|
+
return {
|
|
450
|
+
streamKey: (stream.streamKey),
|
|
451
|
+
audio: (stream.message.value),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
case "video": {
|
|
455
|
+
return {
|
|
456
|
+
streamKey: (stream.streamKey),
|
|
457
|
+
video: (stream.message.value),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
case "playlist": {
|
|
461
|
+
return {
|
|
462
|
+
streamKey: (stream.streamKey),
|
|
463
|
+
playlist: stream.message
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
default: {
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
431
471
|
function debugFormatContext(context) {
|
|
432
472
|
return {
|
|
433
473
|
keys: context.streams.map((stream) => debugFormatStreamMetadata(stream)),
|
|
@@ -511,6 +511,36 @@ export interface AudioSignalGeneratorSettings extends SourceNodeSettings<AudioSi
|
|
|
511
511
|
export declare class AudioSignalGeneratorNode extends SourceMediaNode {
|
|
512
512
|
close(): void;
|
|
513
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* @public
|
|
516
|
+
* Settings for an Video Testcard Generator
|
|
517
|
+
* see: {@link NorskInput.videoTestcard}
|
|
518
|
+
* */
|
|
519
|
+
export interface VideoTestcardGeneratorSettings extends SourceNodeSettings<VideoTestcardGeneratorNode> {
|
|
520
|
+
/** The source name to set in the stream key of the outgoing stream */
|
|
521
|
+
sourceName: string;
|
|
522
|
+
/** The number of frames to send before shutting down */
|
|
523
|
+
numberOfFrames?: number;
|
|
524
|
+
/** Resolution of the test card stream **/
|
|
525
|
+
resolution: {
|
|
526
|
+
width: number;
|
|
527
|
+
height: number;
|
|
528
|
+
};
|
|
529
|
+
/** Framerate of the produced video stream **/
|
|
530
|
+
frameRate: {
|
|
531
|
+
frames: number;
|
|
532
|
+
seconds: number;
|
|
533
|
+
};
|
|
534
|
+
pattern: Pattern;
|
|
535
|
+
}
|
|
536
|
+
declare type Pattern = "black" | "smpte75" | "smpte100";
|
|
537
|
+
/**
|
|
538
|
+
* @public
|
|
539
|
+
* see: {@link NorskInput.audioSignal}
|
|
540
|
+
*/
|
|
541
|
+
export declare class VideoTestcardGeneratorNode extends SourceMediaNode {
|
|
542
|
+
close(): void;
|
|
543
|
+
}
|
|
514
544
|
/**
|
|
515
545
|
* @public
|
|
516
546
|
* Settings for an image file source
|
|
@@ -632,6 +662,11 @@ export interface NorskInput {
|
|
|
632
662
|
* @param settings - Configuration for the RTP input
|
|
633
663
|
*/
|
|
634
664
|
rtp(settings: RtpInputSettings): Promise<RtpInputNode>;
|
|
665
|
+
/**
|
|
666
|
+
* Generate a test video card with a configurable pattern.
|
|
667
|
+
* @param settings - Configuration for the video test card
|
|
668
|
+
*/
|
|
669
|
+
videoTestCard(settings: VideoTestcardGeneratorSettings): Promise<VideoTestcardGeneratorNode>;
|
|
635
670
|
/**
|
|
636
671
|
* Generate a test audio signal with a configurable waveform.
|
|
637
672
|
* @param settings - Configuration for the audio signal
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.FileMp4InputNode = exports.FileImageInputNode = exports.AudioSignalGeneratorNode = exports.BrowserInputNode = exports.M3u8InputNode = exports.UdpTsInputNode = exports.SrtInputNode = exports.FileTsInputNode = exports.FileWebVttInputNode = exports.WhipInputNode = exports.DeckLinkInputNode = exports.RtmpServerInputNode = exports.RtpInputNode = void 0;
|
|
12
|
+
exports.FileMp4InputNode = exports.FileImageInputNode = exports.VideoTestcardGeneratorNode = exports.AudioSignalGeneratorNode = exports.BrowserInputNode = exports.M3u8InputNode = exports.UdpTsInputNode = exports.SrtInputNode = exports.FileTsInputNode = exports.FileWebVttInputNode = exports.WhipInputNode = exports.DeckLinkInputNode = exports.RtmpServerInputNode = exports.RtpInputNode = void 0;
|
|
13
13
|
const media_pb_1 = require("@norskvideo/norsk-api/lib/media_pb");
|
|
14
14
|
const types_1 = require("./types");
|
|
15
15
|
const types_2 = require("../types");
|
|
@@ -1014,6 +1014,78 @@ class AudioSignalGeneratorNode extends common_1.SourceMediaNode {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
}
|
|
1016
1016
|
exports.AudioSignalGeneratorNode = AudioSignalGeneratorNode;
|
|
1017
|
+
function toPattern(p) {
|
|
1018
|
+
switch (p) {
|
|
1019
|
+
case "black":
|
|
1020
|
+
return media_pb_1.TestCardPattern.Black;
|
|
1021
|
+
case "smpte75":
|
|
1022
|
+
return media_pb_1.TestCardPattern.Smpte75;
|
|
1023
|
+
case "smpte100":
|
|
1024
|
+
return media_pb_1.TestCardPattern.Smpte100;
|
|
1025
|
+
default:
|
|
1026
|
+
(0, utils_1.exhaustiveCheck)(p);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* @public
|
|
1031
|
+
* see: {@link NorskInput.audioSignal}
|
|
1032
|
+
*/
|
|
1033
|
+
class VideoTestcardGeneratorNode extends common_1.SourceMediaNode {
|
|
1034
|
+
/** @internal */
|
|
1035
|
+
constructor(settings, client, unregisterNode) {
|
|
1036
|
+
var _a;
|
|
1037
|
+
super(client, settings.onOutboundContextChange);
|
|
1038
|
+
let id = settings.id
|
|
1039
|
+
? (0, utils_1.provideFull)(media_pb_1.MediaNodeId, { id: settings.id })
|
|
1040
|
+
: undefined;
|
|
1041
|
+
const config = (0, utils_1.provideFull)(media_pb_1.TestCardVideoConfiguration, {
|
|
1042
|
+
numberOfFrames: (_a = settings.numberOfFrames) !== null && _a !== void 0 ? _a : 0,
|
|
1043
|
+
realtime: true,
|
|
1044
|
+
sourceName: settings.sourceName,
|
|
1045
|
+
frameRate: (0, utils_1.provideFull)(media_pb_1.FrameRate, settings.frameRate),
|
|
1046
|
+
resolution: (0, utils_1.provideFull)(media_pb_1.Resolution, settings.resolution),
|
|
1047
|
+
pattern: toPattern(settings.pattern)
|
|
1048
|
+
});
|
|
1049
|
+
this.grpcStream = this.client.createInputVideoTestCardGenerator();
|
|
1050
|
+
this.grpcStream.write((0, utils_1.provideFull)(media_pb_1.VideoTestCardGeneratorMessage, (0, utils_1.mkMessageCase)({ setup: (0, utils_1.provideFull)(media_pb_1.TestCardVideoSetup, { id }) })));
|
|
1051
|
+
this.grpcStream.write((0, utils_1.provideFull)(media_pb_1.VideoTestCardGeneratorMessage, (0, utils_1.mkMessageCase)({ configure: config })));
|
|
1052
|
+
this.initialised = new Promise((resolve, reject) => {
|
|
1053
|
+
this.grpcStream.on("data", (data) => {
|
|
1054
|
+
const messageCase = data.message.case;
|
|
1055
|
+
switch (messageCase) {
|
|
1056
|
+
case undefined:
|
|
1057
|
+
break;
|
|
1058
|
+
case "nodeId": {
|
|
1059
|
+
this.id = data.message.value.id;
|
|
1060
|
+
settings.onCreate && settings.onCreate(this);
|
|
1061
|
+
resolve();
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
case "outboundContext": {
|
|
1065
|
+
const context = data.message.value;
|
|
1066
|
+
this.outboundContextChange(context);
|
|
1067
|
+
break;
|
|
1068
|
+
}
|
|
1069
|
+
default:
|
|
1070
|
+
(0, utils_1.exhaustiveCheck)(messageCase);
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
(0, common_1.registerStreamHandlers)(this.grpcStream, () => unregisterNode(this), "videotestcard", reject, settings);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
/** @internal */
|
|
1077
|
+
static create(settings, client, unregisterNode) {
|
|
1078
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1079
|
+
const node = new VideoTestcardGeneratorNode(settings, client, unregisterNode);
|
|
1080
|
+
yield node.initialised;
|
|
1081
|
+
return node;
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
close() {
|
|
1085
|
+
this.grpcStream.cancel();
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
exports.VideoTestcardGeneratorNode = VideoTestcardGeneratorNode;
|
|
1017
1089
|
/**
|
|
1018
1090
|
* @public
|
|
1019
1091
|
* see: {@link NorskInput.fileImage}
|
|
@@ -376,11 +376,11 @@ declare enum PlaylistPath {
|
|
|
376
376
|
Ts = 1
|
|
377
377
|
}
|
|
378
378
|
declare class CmafNodeBase<ClientMessage, Pins extends string, T extends MediaNodeState> extends AutoProcessorMediaNode<Pins> {
|
|
379
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) =>
|
|
379
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) => Promise<boolean>, _playlistPath: PlaylistPath, subscribedStreamsChangedFn?: (streams: StreamMetadata[]) => void);
|
|
380
380
|
close(): void;
|
|
381
381
|
}
|
|
382
382
|
declare class CmafNodeWithPlaylist<ClientMessage, Pins extends string, T extends MediaNodeState> extends CmafNodeBase<ClientMessage, Pins, T> {
|
|
383
|
-
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) =>
|
|
383
|
+
constructor(client: MediaClient, unregisterNode: (node: MediaNodeState) => void, settings: ProcessorNodeSettings<T> & StreamStatisticsMixin, grpcInit: () => grpc.ClientDuplexStream<ClientMessage, HlsOutputEvent>, subscribeFn: (subscription: Subscription) => Promise<boolean>, playlistPath: PlaylistPath, sessionId: string);
|
|
384
384
|
/**
|
|
385
385
|
* @public
|
|
386
386
|
* Returns the URL to the HLS playlist entry. Note this can only be evaluated once the stream is active as it
|